1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
18 #include "libgame/libgame.h"
25 #define MAX_FILENAME_LEN 256 /* maximal filename length */
26 #define MAX_LINE_LEN 1000 /* maximal input line length */
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
29 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
30 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
32 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
33 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
35 /* file identifier strings */
36 #define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_2.0"
37 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
38 #define TAPE_COOKIE "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
39 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
40 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
41 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
42 /* old file identifiers for backward compatibility */
43 #define LEVEL_COOKIE_10 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
44 #define LEVEL_COOKIE_12 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
45 #define LEVEL_COOKIE_14 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
46 #define TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
48 /* file names and filename extensions */
49 #if !defined(PLATFORM_MSDOS)
50 #define LEVELSETUP_DIRECTORY "levelsetup"
51 #define SETUP_FILENAME "setup.conf"
52 #define LEVELSETUP_FILENAME "levelsetup.conf"
53 #define LEVELINFO_FILENAME "levelinfo.conf"
54 #define LEVELFILE_EXTENSION "level"
55 #define TAPEFILE_EXTENSION "tape"
56 #define SCOREFILE_EXTENSION "score"
58 #define LEVELSETUP_DIRECTORY "lvlsetup"
59 #define SETUP_FILENAME "setup.cnf"
60 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
61 #define LEVELINFO_FILENAME "lvlinfo.cnf"
62 #define LEVELFILE_EXTENSION "lvl"
63 #define TAPEFILE_EXTENSION "tap"
64 #define SCOREFILE_EXTENSION "sco"
67 #if defined(PLATFORM_WIN32)
69 #define S_IRGRP S_IRUSR
72 #define S_IROTH S_IRUSR
75 #define S_IWGRP S_IWUSR
78 #define S_IWOTH S_IWUSR
81 #define S_IXGRP S_IXUSR
84 #define S_IXOTH S_IXUSR
86 #endif /* PLATFORM_WIN32 */
88 /* file permissions for newly written files */
89 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
90 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
91 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
92 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
93 #define SCORE_PERMS LEVEL_PERMS
94 #define TAPE_PERMS LEVEL_PERMS
95 #define SETUP_PERMS LEVEL_PERMS
97 /* sort priorities of level series (also used as level series classes) */
98 #define LEVELCLASS_TUTORIAL_START 10
99 #define LEVELCLASS_TUTORIAL_END 99
100 #define LEVELCLASS_CLASSICS_START 100
101 #define LEVELCLASS_CLASSICS_END 199
102 #define LEVELCLASS_CONTRIBUTION_START 200
103 #define LEVELCLASS_CONTRIBUTION_END 299
104 #define LEVELCLASS_USER_START 300
105 #define LEVELCLASS_USER_END 399
106 #define LEVELCLASS_BD_START 400
107 #define LEVELCLASS_BD_END 499
108 #define LEVELCLASS_EM_START 500
109 #define LEVELCLASS_EM_END 599
110 #define LEVELCLASS_SP_START 600
111 #define LEVELCLASS_SP_END 699
112 #define LEVELCLASS_DX_START 700
113 #define LEVELCLASS_DX_END 799
115 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
116 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
117 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
118 #define LEVELCLASS_USER LEVELCLASS_USER_START
119 #define LEVELCLASS_BD LEVELCLASS_BD_START
120 #define LEVELCLASS_EM LEVELCLASS_EM_START
121 #define LEVELCLASS_SP LEVELCLASS_SP_START
122 #define LEVELCLASS_DX LEVELCLASS_DX_START
124 #define LEVELCLASS_UNDEFINED 999
126 #define NUM_LEVELCLASS_DESC 8
127 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
139 #define IS_LEVELCLASS_TUTORIAL(p) \
140 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
141 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
142 #define IS_LEVELCLASS_CLASSICS(p) \
143 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
144 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
145 #define IS_LEVELCLASS_CONTRIBUTION(p) \
146 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
147 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
148 #define IS_LEVELCLASS_USER(p) \
149 ((p)->sort_priority >= LEVELCLASS_USER_START && \
150 (p)->sort_priority <= LEVELCLASS_USER_END)
151 #define IS_LEVELCLASS_BD(p) \
152 ((p)->sort_priority >= LEVELCLASS_BD_START && \
153 (p)->sort_priority <= LEVELCLASS_BD_END)
154 #define IS_LEVELCLASS_EM(p) \
155 ((p)->sort_priority >= LEVELCLASS_EM_START && \
156 (p)->sort_priority <= LEVELCLASS_EM_END)
157 #define IS_LEVELCLASS_SP(p) \
158 ((p)->sort_priority >= LEVELCLASS_SP_START && \
159 (p)->sort_priority <= LEVELCLASS_SP_END)
160 #define IS_LEVELCLASS_DX(p) \
161 ((p)->sort_priority >= LEVELCLASS_DX_START && \
162 (p)->sort_priority <= LEVELCLASS_DX_END)
164 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
165 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
166 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
167 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
168 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
169 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
170 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
171 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
172 LEVELCLASS_UNDEFINED)
174 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
175 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
176 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
177 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
178 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
179 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
180 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
181 IS_LEVELCLASS_USER(n) ? FC_RED : \
184 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
185 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
186 IS_LEVELCLASS_BD(n) ? 2 : \
187 IS_LEVELCLASS_EM(n) ? 3 : \
188 IS_LEVELCLASS_SP(n) ? 4 : \
189 IS_LEVELCLASS_DX(n) ? 5 : \
190 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
191 IS_LEVELCLASS_USER(n) ? 7 : \
194 static int getFileVersionFromCookieString(const char *cookie)
196 const char *ptr_cookie1, *ptr_cookie2;
197 const char *pattern1 = "_FILE_VERSION_";
198 const char *pattern2 = "?.?";
199 const int len_cookie = strlen(cookie);
200 const int len_pattern1 = strlen(pattern1);
201 const int len_pattern2 = strlen(pattern2);
202 const int len_pattern = len_pattern1 + len_pattern2;
203 int version_major, version_minor;
205 if (len_cookie <= len_pattern)
208 ptr_cookie1 = &cookie[len_cookie - len_pattern];
209 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
211 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
214 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
215 ptr_cookie2[1] != '.' ||
216 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
219 version_major = ptr_cookie2[0] - '0';
220 version_minor = ptr_cookie2[2] - '0';
222 return (version_major * 10 + version_minor);
225 boolean checkCookieString(const char *cookie, const char *template)
227 const char *pattern = "_FILE_VERSION_?.?";
228 const int len_cookie = strlen(cookie);
229 const int len_template = strlen(template);
230 const int len_pattern = strlen(pattern);
232 if (len_cookie != len_template)
235 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
241 char *getLevelClassDescription(struct LevelDirInfo *ldi)
243 int position = ldi->sort_priority / 100;
245 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
246 return levelclass_desc[position];
248 return "Unknown Level Class";
251 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
252 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
254 static char *getSetupDir()
256 return getUserDataDir();
259 static char *getUserLevelDir(char *level_subdir)
261 static char *userlevel_dir = NULL;
262 char *data_dir = getUserDataDir();
263 char *userlevel_subdir = LEVELS_DIRECTORY;
268 if (strlen(level_subdir) > 0)
269 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
271 userlevel_dir = getPath2(data_dir, userlevel_subdir);
273 return userlevel_dir;
276 static char *getTapeDir(char *level_subdir)
278 static char *tape_dir = NULL;
279 char *data_dir = getUserDataDir();
280 char *tape_subdir = TAPES_DIRECTORY;
285 if (strlen(level_subdir) > 0)
286 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
288 tape_dir = getPath2(data_dir, tape_subdir);
293 static char *getScoreDir(char *level_subdir)
295 static char *score_dir = NULL;
296 char *data_dir = options.rw_base_directory;
297 char *score_subdir = SCORES_DIRECTORY;
302 if (strlen(level_subdir) > 0)
303 score_dir = getPath3(data_dir, score_subdir, level_subdir);
305 score_dir = getPath2(data_dir, score_subdir);
310 static char *getLevelSetupDir(char *level_subdir)
312 static char *levelsetup_dir = NULL;
313 char *data_dir = getUserDataDir();
314 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
317 free(levelsetup_dir);
319 if (strlen(level_subdir) > 0)
320 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
322 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
324 return levelsetup_dir;
327 static char *getLevelFilename(int nr)
329 static char *filename = NULL;
330 char basename[MAX_FILENAME_LEN];
332 if (filename != NULL)
335 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
336 filename = getPath3((leveldir_current->user_defined ?
337 getUserLevelDir("") :
338 options.level_directory),
339 leveldir_current->fullpath,
345 static char *getTapeFilename(int nr)
347 static char *filename = NULL;
348 char basename[MAX_FILENAME_LEN];
350 if (filename != NULL)
353 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
354 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
359 static char *getScoreFilename(int nr)
361 static char *filename = NULL;
362 char basename[MAX_FILENAME_LEN];
364 if (filename != NULL)
367 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
368 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
373 static void InitTapeDirectory(char *level_subdir)
375 createDirectory(getUserDataDir(), "user data");
376 createDirectory(getTapeDir(""), "main tape");
377 createDirectory(getTapeDir(level_subdir), "level tape");
380 static void InitScoreDirectory(char *level_subdir)
382 createDirectory(getScoreDir(""), "main score");
383 createDirectory(getScoreDir(level_subdir), "level score");
386 static void InitUserLevelDirectory(char *level_subdir)
388 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
390 createDirectory(getUserDataDir(), "user data");
391 createDirectory(getUserLevelDir(""), "main user level");
392 createDirectory(getUserLevelDir(level_subdir), "user level");
398 static void InitLevelSetupDirectory(char *level_subdir)
400 createDirectory(getUserDataDir(), "user data");
401 createDirectory(getLevelSetupDir(""), "main level setup");
402 createDirectory(getLevelSetupDir(level_subdir), "level setup");
405 static void setLevelInfoToDefaults()
409 level.file_version = FILE_VERSION_ACTUAL;
410 level.game_version = GAME_VERSION_ACTUAL;
412 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
413 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
414 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
416 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
417 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
419 for(x=0; x<MAX_LEV_FIELDX; x++)
420 for(y=0; y<MAX_LEV_FIELDY; y++)
421 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
424 level.gems_needed = 0;
425 level.amoeba_speed = 10;
426 level.time_magic_wall = 10;
427 level.time_wheel = 10;
428 level.time_light = 10;
429 level.time_timegate = 10;
430 level.amoeba_content = EL_DIAMANT;
431 level.double_speed = FALSE;
432 level.gravity = FALSE;
434 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
435 level.name[i] = '\0';
436 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
437 level.author[i] = '\0';
439 strcpy(level.name, NAMELESS_LEVEL_NAME);
440 strcpy(level.author, ANONYMOUS_NAME);
442 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
445 level.num_yam_contents = STD_ELEMENT_CONTENTS;
446 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
449 level.yam_content[i][x][y] =
450 (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
452 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
453 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
454 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
456 BorderElement = EL_BETON;
458 /* try to determine better author name than 'anonymous' */
459 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
461 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
462 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
466 switch (LEVELCLASS(leveldir_current))
468 case LEVELCLASS_TUTORIAL:
469 strcpy(level.author, PROGRAM_AUTHOR_STRING);
472 case LEVELCLASS_CONTRIBUTION:
473 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
474 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
477 case LEVELCLASS_USER:
478 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
479 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
483 /* keep default value */
489 static int checkLevelElement(int element)
491 if (element >= EL_FIRST_RUNTIME_EL)
493 Error(ERR_WARN, "invalid level element %d", element);
494 element = EL_CHAR_FRAGE;
500 void OLD_LoadLevel(int level_nr)
503 char *filename = getLevelFilename(level_nr);
504 char cookie[MAX_LINE_LEN];
505 char chunk_name[CHUNK_ID_LEN + 1];
506 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
507 int file_version = FILE_VERSION_ACTUAL;
511 /* always start with reliable default values */
512 setLevelInfoToDefaults();
514 if (!(file = fopen(filename, MODE_READ)))
516 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
520 /* check file identifier */
521 fgets(cookie, MAX_LINE_LEN, file);
522 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
523 cookie[strlen(cookie) - 1] = '\0';
526 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
527 file_version = FILE_VERSION_1_0;
528 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
529 file_version = FILE_VERSION_1_2;
530 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
532 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
537 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
539 Error(ERR_WARN, "unknown format of level file '%s'", filename);
544 file_version = getFileVersionFromCookieString(cookie);
547 level.file_version = file_version;
549 /* read chunk "HEAD" */
550 if (file_version >= FILE_VERSION_1_2)
552 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
553 if (strcmp(chunk_name, "HEAD") || chunk_size != LEVEL_HEADER_SIZE)
555 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
561 lev_fieldx = level.fieldx = fgetc(file);
562 lev_fieldy = level.fieldy = fgetc(file);
564 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
565 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
567 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
568 level.name[i] = fgetc(file);
569 level.name[MAX_LEVEL_NAME_LEN] = 0;
571 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
572 level.score[i] = fgetc(file);
574 level.num_yam_contents = STD_ELEMENT_CONTENTS;
575 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
581 if (i < STD_ELEMENT_CONTENTS)
582 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
584 level.yam_content[i][x][y] = EL_LEERRAUM;
589 level.amoeba_speed = fgetc(file);
590 level.time_magic_wall = fgetc(file);
591 level.time_wheel = fgetc(file);
592 level.amoeba_content = checkLevelElement(fgetc(file));
593 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
594 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
596 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
598 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
601 if (file_version >= FILE_VERSION_1_2)
603 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
605 /* look for optional author chunk */
606 if (strcmp(chunk_name, "AUTH") == 0 && chunk_size == MAX_LEVEL_AUTHOR_LEN)
608 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
609 level.author[i] = fgetc(file);
610 level.author[MAX_LEVEL_NAME_LEN] = 0;
612 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
615 /* look for optional content chunk */
616 if (strcmp(chunk_name, "CONT") == 0 &&
617 chunk_size == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
620 level.num_yam_contents = fgetc(file);
624 if (level.num_yam_contents < 1 ||
625 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
628 printf("WARNING: num_yam_contents == %d (corrected)\n",
629 level.num_yam_contents);
631 level.num_yam_contents = STD_ELEMENT_CONTENTS;
634 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
637 level.yam_content[i][x][y] =
638 checkLevelElement(encoding_16bit ?
639 getFile16BitInteger(file,
640 BYTE_ORDER_BIG_ENDIAN) :
643 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
646 /* next check body chunk identifier and chunk size */
647 if (strcmp(chunk_name, "BODY") != 0 ||
648 chunk_size != lev_fieldx * lev_fieldy)
650 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
656 /* clear all other level fields (needed if resized in level editor later) */
657 for(x=0; x<MAX_LEV_FIELDX; x++)
658 for(y=0; y<MAX_LEV_FIELDY; y++)
659 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
661 /* now read in the valid level fields from level file */
662 for(y=0; y<lev_fieldy; y++)
663 for(x=0; x<lev_fieldx; x++)
664 Feld[x][y] = Ur[x][y] =
665 checkLevelElement(encoding_16bit ?
666 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
671 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
672 IS_LEVELCLASS_USER(leveldir_current))
674 /* for user contributed and private levels, use the version of
675 the game engine the levels were created for */
676 level.game_version = file_version;
678 /* player was faster than monsters in pre-1.0 levels */
679 if (file_version == FILE_VERSION_1_0)
681 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
682 Error(ERR_WARN, "using high speed movement for player");
683 level.double_speed = TRUE;
688 /* always use the latest version of the game engine for all but
689 user contributed and private levels */
690 level.game_version = GAME_VERSION_ACTUAL;
693 /* determine border element for this level */
697 static void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
703 static void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
709 static int LoadLevel_HEAD(struct LevelInfo *level, FILE *file, int chunk_size)
713 lev_fieldx = level->fieldx = fgetc(file);
714 lev_fieldy = level->fieldy = fgetc(file);
716 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
717 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
719 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
720 level->name[i] = fgetc(file);
721 level->name[MAX_LEVEL_NAME_LEN] = 0;
723 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
724 level->score[i] = fgetc(file);
726 level->num_yam_contents = STD_ELEMENT_CONTENTS;
727 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
730 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
732 level->amoeba_speed = fgetc(file);
733 level->time_magic_wall = fgetc(file);
734 level->time_wheel = fgetc(file);
735 level->amoeba_content = checkLevelElement(fgetc(file));
736 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
737 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
739 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
741 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
746 static int LoadLevel_AUTH(struct LevelInfo *level, FILE *file, int chunk_size)
750 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
751 level->author[i] = fgetc(file);
752 level->author[MAX_LEVEL_NAME_LEN] = 0;
757 static int LoadLevel_CONT(struct LevelInfo *level, FILE *file, int chunk_size)
761 int content_size_type1 = MAX_ELEMENT_CONTENTS * 3 * 3;
763 int chunk_size_expected = header_size + content_size_type1;
765 /* Note: "chunk_size" was wrong before version 2.0 when elements are
766 stored with 16-bit encoding (and should be twice as big then).
767 Even worse, playfield data was stored 16-bit when only yamyam content
768 contained 16-bit elements and vice versa. */
770 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
771 chunk_size_expected += content_size_type1;
773 if (chunk_size_expected != chunk_size)
775 ReadUnusedBytesFromFile(file, chunk_size);
776 return chunk_size_expected;
780 level->num_yam_contents = fgetc(file);
784 /* correct invalid number of content fields -- should never happen */
785 if (level->num_yam_contents < 1 ||
786 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
787 level->num_yam_contents = STD_ELEMENT_CONTENTS;
789 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
792 level->yam_content[i][x][y] =
793 checkLevelElement(level->encoding_16bit_field ?
794 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
799 static int LoadLevel_BODY(struct LevelInfo *level, FILE *file, int chunk_size)
802 int chunk_size_expected = level->fieldx * level->fieldy;
804 /* Note: "chunk_size" was wrong before version 2.0 when elements are
805 stored with 16-bit encoding (and should be twice as big then).
806 Even worse, playfield data was stored 16-bit when only yamyam content
807 contained 16-bit elements and vice versa. */
809 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
810 chunk_size_expected *= 2;
812 if (chunk_size_expected != chunk_size)
814 ReadUnusedBytesFromFile(file, chunk_size);
815 return chunk_size_expected;
818 for(y=0; y<level->fieldy; y++)
819 for(x=0; x<level->fieldx; x++)
820 Feld[x][y] = Ur[x][y] =
821 checkLevelElement(level->encoding_16bit_field ?
822 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
827 static int LoadLevel_CNT2(struct LevelInfo *level, FILE *file, int chunk_size)
831 int num_contents, content_xsize, content_ysize;
832 int content_array[MAX_ELEMENT_CONTENTS][3][3];
834 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
835 num_contents = fgetc(file);
836 content_xsize = fgetc(file);
837 content_ysize = fgetc(file);
838 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
840 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
843 content_array[i][x][y] =
844 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
846 /* correct invalid number of content fields -- should never happen */
847 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
848 num_contents = STD_ELEMENT_CONTENTS;
850 if (element == EL_MAMPFER)
852 level->num_yam_contents = num_contents;
854 for(i=0; i<num_contents; i++)
857 level->yam_content[i][x][y] = content_array[i][x][y];
859 else if (element == EL_AMOEBE_BD)
861 level->amoeba_content = content_array[0][0][0];
865 Error(ERR_WARN, "cannot load content for element '%d'", element);
871 void LoadLevel(int level_nr)
873 char *filename = getLevelFilename(level_nr);
874 char cookie[MAX_LINE_LEN];
875 char chunk_name[CHUNK_ID_LEN + 1];
879 /* always start with reliable default values */
880 setLevelInfoToDefaults();
882 if (!(file = fopen(filename, MODE_READ)))
884 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
888 /* check file identifier */
889 fgets(cookie, MAX_LINE_LEN, file);
890 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
891 cookie[strlen(cookie) - 1] = '\0';
893 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
895 Error(ERR_WARN, "unknown format of level file '%s'", filename);
900 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
902 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
907 if (level.file_version < FILE_VERSION_1_2)
909 /* level files from versions before 1.2.0 without chunk structure */
910 LoadLevel_HEAD(&level, file, LEVEL_HEADER_SIZE);
911 LoadLevel_BODY(&level, file, level.fieldx * level.fieldy);
919 int (*loader)(struct LevelInfo *, FILE *, int);
923 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
924 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
925 { "CONT", -1, LoadLevel_CONT },
926 { "BODY", -1, LoadLevel_BODY },
927 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
931 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
935 while (chunk_info[i].name != NULL &&
936 strcmp(chunk_name, chunk_info[i].name) != 0)
939 if (chunk_info[i].name == NULL)
941 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
942 chunk_name, filename);
943 ReadUnusedBytesFromFile(file, chunk_size);
945 else if (chunk_info[i].size != -1 &&
946 chunk_info[i].size != chunk_size)
948 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
949 chunk_size, chunk_name, filename);
950 ReadUnusedBytesFromFile(file, chunk_size);
954 /* call function to load this level chunk */
955 int chunk_size_expected =
956 (chunk_info[i].loader)(&level, file, chunk_size);
958 /* the size of some chunks cannot be checked before reading other
959 chunks first (like "HEAD" and "BODY") or before reading some
960 header information first (like "CONT"), so check them here */
961 if (chunk_size_expected != chunk_size)
963 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
964 chunk_size, chunk_name, filename);
972 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
973 IS_LEVELCLASS_USER(leveldir_current))
975 /* for user contributed and private levels, use the version of
976 the game engine the levels were created for */
977 level.game_version = level.file_version;
979 /* player was faster than monsters in pre-1.0 levels */
980 if (level.file_version == FILE_VERSION_1_0)
982 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
983 Error(ERR_WARN, "using high speed movement for player");
984 level.double_speed = TRUE;
989 /* always use the latest version of the game engine for all but
990 user contributed and private levels */
991 level.game_version = GAME_VERSION_ACTUAL;
994 /* determine border element for this level */
998 void OLD_SaveLevel(int level_nr)
1001 char *filename = getLevelFilename(level_nr);
1003 boolean encoding_16bit_amoeba = FALSE;
1004 boolean encoding_16bit_yamyam = FALSE;
1006 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
1007 char *oldest_possible_cookie;
1010 if (!(file = fopen(filename, MODE_WRITE)))
1012 Error(ERR_WARN, "cannot save level file '%s'", filename);
1016 /* check yam content for 16-bit elements */
1017 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1020 if (level.yam_content[i][x][y] > 255)
1021 encoding_16bit = TRUE;
1023 /* check level field for 16-bit elements */
1024 for(y=0; y<lev_fieldy; y++)
1025 for(x=0; x<lev_fieldx; x++)
1027 encoding_16bit = TRUE;
1029 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
1031 fputs(oldest_possible_cookie, file); /* file identifier */
1034 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1036 fputc(level.fieldx, file);
1037 fputc(level.fieldy, file);
1039 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1040 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1042 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1043 fputc(level.name[i], file);
1044 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1045 fputc(level.score[i], file);
1046 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1049 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1050 fputc(level.amoeba_speed, file);
1051 fputc(level.time_magic_wall, file);
1052 fputc(level.time_wheel, file);
1053 fputc(level.amoeba_content, file);
1054 fputc((level.double_speed ? 1 : 0), file);
1055 fputc((level.gravity ? 1 : 0), file);
1057 fputc((encoding_16bit ? 1 : 0), file);
1059 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1062 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1064 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1065 fputc(level.author[i], file);
1067 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1068 BYTE_ORDER_BIG_ENDIAN);
1070 fputc(EL_MAMPFER, file);
1071 fputc(level.num_yam_contents, file);
1075 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1079 putFile16BitInteger(file, level.yam_content[i][x][y],
1080 BYTE_ORDER_BIG_ENDIAN);
1082 fputc(level.yam_content[i][x][y], file);
1084 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1086 for(y=0; y<lev_fieldy; y++)
1087 for(x=0; x<lev_fieldx; x++)
1089 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1091 fputc(Ur[x][y], file);
1095 chmod(filename, LEVEL_PERMS);
1098 static void SaveLevel_HEAD(struct LevelInfo *level, FILE *file)
1102 fputc(level->fieldx, file);
1103 fputc(level->fieldy, file);
1105 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
1106 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
1108 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1109 fputc(level->name[i], file);
1111 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1112 fputc(level->score[i], file);
1114 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1117 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
1118 level->yam_content[i][x][y]),
1120 fputc(level->amoeba_speed, file);
1121 fputc(level->time_magic_wall, file);
1122 fputc(level->time_wheel, file);
1123 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
1125 fputc((level->double_speed ? 1 : 0), file);
1126 fputc((level->gravity ? 1 : 0), file);
1128 fputc((level->encoding_16bit_field ? 1 : 0), file);
1130 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1133 static void SaveLevel_AUTH(struct LevelInfo *level, FILE *file)
1137 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1138 fputc(level->author[i], file);
1142 static void SaveLevel_CONT(struct LevelInfo *level, FILE *file)
1146 fputc(EL_MAMPFER, file);
1147 fputc(level->num_yam_contents, file);
1151 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1154 if (level->encoding_16bit_field)
1155 putFile16BitInteger(file, level->yam_content[i][x][y],
1156 BYTE_ORDER_BIG_ENDIAN);
1158 fputc(level->yam_content[i][x][y], file);
1162 static void SaveLevel_BODY(struct LevelInfo *level, FILE *file)
1166 for(y=0; y<lev_fieldy; y++)
1167 for(x=0; x<lev_fieldx; x++)
1168 if (level->encoding_16bit_field)
1169 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1171 fputc(Ur[x][y], file);
1174 static void SaveLevel_CNT2(struct LevelInfo *level, FILE *file, int element)
1177 int num_contents, content_xsize, content_ysize;
1178 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1180 if (element == EL_MAMPFER)
1182 num_contents = level->num_yam_contents;
1186 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1189 content_array[i][x][y] = level->yam_content[i][x][y];
1191 else if (element == EL_AMOEBE_BD)
1197 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1200 content_array[i][x][y] = EL_LEERRAUM;
1201 content_array[0][0][0] = level->amoeba_content;
1205 /* chunk header already written -- write empty chunk data */
1206 for(i=0; i<LEVEL_CHUNK_CNT2_SIZE; i++)
1209 Error(ERR_WARN, "cannot save content for element '%d'", element);
1213 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
1214 fputc(num_contents, file);
1215 fputc(content_xsize, file);
1216 fputc(content_ysize, file);
1217 for(i=0; i<LEVEL_CHUNK_CNT2_UNUSED; i++)
1220 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1223 putFile16BitInteger(file, content_array[i][x][y],
1224 BYTE_ORDER_BIG_ENDIAN);
1227 void SaveLevel(int level_nr)
1230 char *filename = getLevelFilename(level_nr);
1234 if (!(file = fopen(filename, MODE_WRITE)))
1236 Error(ERR_WARN, "cannot save level file '%s'", filename);
1240 /* check level field for 16-bit elements */
1241 for(y=0; y<level.fieldy; y++)
1242 for(x=0; x<level.fieldx; x++)
1244 level.encoding_16bit_field = TRUE;
1246 /* check yamyam content for 16-bit elements */
1247 for(i=0; i<level.num_yam_contents; i++)
1250 if (level.yam_content[i][x][y] > 255)
1251 level.encoding_16bit_yamyam = TRUE;
1253 /* check amoeba content for 16-bit elements */
1254 if (level.amoeba_content > 255)
1255 level.encoding_16bit_amoeba = TRUE;
1257 fputs(LEVEL_COOKIE, file); /* file identifier */
1260 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1261 SaveLevel_HEAD(&level, file);
1263 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1264 SaveLevel_AUTH(&level, file);
1267 if (level.encoding_16bit_field) /* obsolete since new "CNT2" chunk */
1269 chunk_size = 4 + 2 * (MAX_ELEMENT_CONTENTS * 3 * 3);
1271 putFileChunk(file, "CONT", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1272 SaveLevel_CONT(&level, file);
1277 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1278 putFileChunk(file, "BODY", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1279 SaveLevel_BODY(&level, file);
1281 if (level.encoding_16bit_yamyam ||
1282 level.num_yam_contents != STD_ELEMENT_CONTENTS)
1284 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1285 SaveLevel_CNT2(&level, file, EL_MAMPFER);
1288 if (level.encoding_16bit_amoeba)
1290 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1291 SaveLevel_CNT2(&level, file, EL_AMOEBE_BD);
1296 chmod(filename, LEVEL_PERMS);
1299 void LoadTape(int level_nr)
1302 char *filename = getTapeFilename(level_nr);
1303 char cookie[MAX_LINE_LEN];
1304 char chunk_name[CHUNK_ID_LEN + 1];
1306 int num_participating_players;
1307 int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1310 /* always start with reliable default values (empty tape) */
1311 tape.file_version = FILE_VERSION_ACTUAL;
1312 tape.game_version = GAME_VERSION_ACTUAL;
1315 /* default values (also for pre-1.2 tapes) with only the first player */
1316 tape.player_participates[0] = TRUE;
1317 for(i=1; i<MAX_PLAYERS; i++)
1318 tape.player_participates[i] = FALSE;
1320 /* at least one (default: the first) player participates in every tape */
1321 num_participating_players = 1;
1323 if (!(file = fopen(filename, MODE_READ)))
1326 /* check file identifier */
1327 fgets(cookie, MAX_LINE_LEN, file);
1328 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1329 cookie[strlen(cookie) - 1] = '\0';
1332 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
1333 file_version = FILE_VERSION_1_0;
1334 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
1336 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1341 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1343 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1348 file_version = getFileVersionFromCookieString(cookie);
1351 tape.file_version = file_version;
1352 tape.game_version = file_version;
1354 /* read chunk "HEAD" */
1355 if (file_version >= FILE_VERSION_1_2)
1357 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1358 if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1360 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1366 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1367 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1368 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1370 /* read header fields that are new since version 1.2 */
1371 if (file_version >= FILE_VERSION_1_2)
1373 byte store_participating_players = fgetc(file);
1375 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
1378 /* since version 1.2, tapes store which players participate in the tape */
1379 num_participating_players = 0;
1380 for(i=0; i<MAX_PLAYERS; i++)
1382 tape.player_participates[i] = FALSE;
1384 if (store_participating_players & (1 << i))
1386 tape.player_participates[i] = TRUE;
1387 num_participating_players++;
1392 tape.level_nr = level_nr;
1394 tape.changed = FALSE;
1396 tape.recording = FALSE;
1397 tape.playing = FALSE;
1398 tape.pausing = FALSE;
1400 /* read chunk "BODY" */
1401 if (file_version >= FILE_VERSION_1_2)
1403 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1404 if (strcmp(chunk_name, "BODY") ||
1405 chunk_size != (num_participating_players + 1) * tape.length)
1407 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1413 for(i=0; i<tape.length; i++)
1415 if (i >= MAX_TAPELEN)
1418 for(j=0; j<MAX_PLAYERS; j++)
1420 tape.pos[i].action[j] = MV_NO_MOVING;
1422 if (tape.player_participates[j])
1423 tape.pos[i].action[j] = fgetc(file);
1426 tape.pos[i].delay = fgetc(file);
1428 if (file_version == FILE_VERSION_1_0)
1430 /* eliminate possible diagonal moves in old tapes */
1431 /* this is only for backward compatibility */
1433 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1434 byte action = tape.pos[i].action[0];
1435 int k, num_moves = 0;
1439 if (action & joy_dir[k])
1441 tape.pos[i + num_moves].action[0] = joy_dir[k];
1443 tape.pos[i + num_moves].delay = 0;
1452 tape.length += num_moves;
1462 if (i != tape.length)
1463 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1465 tape.length_seconds = GetTapeLength();
1468 void SaveTape(int level_nr)
1471 char *filename = getTapeFilename(level_nr);
1473 boolean new_tape = TRUE;
1474 byte store_participating_players;
1475 int num_participating_players;
1477 InitTapeDirectory(leveldir_current->filename);
1479 /* if a tape still exists, ask to overwrite it */
1480 if (access(filename, F_OK) == 0)
1483 if (!Request("Replace old tape ?", REQ_ASK))
1487 /* count number of players and set corresponding bits for compact storage */
1488 store_participating_players = 0;
1489 num_participating_players = 0;
1490 for(i=0; i<MAX_PLAYERS; i++)
1492 if (tape.player_participates[i])
1494 num_participating_players++;
1495 store_participating_players |= (1 << i);
1499 if (!(file = fopen(filename, MODE_WRITE)))
1501 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1505 fputs(TAPE_COOKIE, file); /* file identifier */
1508 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1510 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1511 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1512 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1514 fputc(store_participating_players, file);
1516 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1519 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1520 BYTE_ORDER_BIG_ENDIAN);
1522 for(i=0; i<tape.length; i++)
1526 for(j=0; j<MAX_PLAYERS; j++)
1527 if (tape.player_participates[j])
1528 fputc(tape.pos[i].action[j], file);
1530 fputc(tape.pos[i].delay, file);
1535 chmod(filename, TAPE_PERMS);
1537 tape.changed = FALSE;
1540 Request("tape saved !", REQ_CONFIRM);
1543 void LoadScore(int level_nr)
1546 char *filename = getScoreFilename(level_nr);
1547 char cookie[MAX_LINE_LEN];
1548 char line[MAX_LINE_LEN];
1552 /* always start with reliable default values */
1553 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1555 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1556 highscore[i].Score = 0;
1559 if (!(file = fopen(filename, MODE_READ)))
1562 /* check file identifier */
1563 fgets(cookie, MAX_LINE_LEN, file);
1564 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1565 cookie[strlen(cookie) - 1] = '\0';
1568 if (strcmp(cookie, SCORE_COOKIE) != 0)
1570 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1575 if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1577 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1583 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1585 fscanf(file, "%d", &highscore[i].Score);
1586 fgets(line, MAX_LINE_LEN, file);
1588 if (line[strlen(line) - 1] == '\n')
1589 line[strlen(line) - 1] = '\0';
1591 for (line_ptr = line; *line_ptr; line_ptr++)
1593 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1595 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1596 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1605 void SaveScore(int level_nr)
1608 char *filename = getScoreFilename(level_nr);
1611 InitScoreDirectory(leveldir_current->filename);
1613 if (!(file = fopen(filename, MODE_WRITE)))
1615 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1619 fprintf(file, "%s\n\n", SCORE_COOKIE);
1621 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1622 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1626 chmod(filename, SCORE_PERMS);
1629 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1630 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1631 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1632 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1633 #define TOKEN_STR_PLAYER_PREFIX "player_"
1635 #define TOKEN_VALUE_POSITION 30
1638 #define SETUP_TOKEN_PLAYER_NAME 0
1639 #define SETUP_TOKEN_SOUND 1
1640 #define SETUP_TOKEN_SOUND_LOOPS 2
1641 #define SETUP_TOKEN_SOUND_MUSIC 3
1642 #define SETUP_TOKEN_SOUND_SIMPLE 4
1645 #define SETUP_TOKEN_TOONS 5
1646 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1649 #define SETUP_TOKEN_SCROLL_DELAY 5
1650 #define SETUP_TOKEN_SOFT_SCROLLING 6
1651 #define SETUP_TOKEN_FADING 7
1652 #define SETUP_TOKEN_AUTORECORD 8
1653 #define SETUP_TOKEN_QUICK_DOORS 9
1654 #define SETUP_TOKEN_TEAM_MODE 10
1655 #define SETUP_TOKEN_HANDICAP 11
1656 #define SETUP_TOKEN_TIME_LIMIT 12
1657 #define SETUP_TOKEN_FULLSCREEN 13
1660 #define SETUP_TOKEN_USE_JOYSTICK 14
1661 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1662 #define SETUP_TOKEN_JOY_XLEFT 16
1663 #define SETUP_TOKEN_JOY_XMIDDLE 17
1664 #define SETUP_TOKEN_JOY_XRIGHT 18
1665 #define SETUP_TOKEN_JOY_YUPPER 19
1666 #define SETUP_TOKEN_JOY_YMIDDLE 20
1667 #define SETUP_TOKEN_JOY_YLOWER 21
1668 #define SETUP_TOKEN_JOY_SNAP 22
1669 #define SETUP_TOKEN_JOY_BOMB 23
1670 #define SETUP_TOKEN_KEY_LEFT 24
1671 #define SETUP_TOKEN_KEY_RIGHT 25
1672 #define SETUP_TOKEN_KEY_UP 26
1673 #define SETUP_TOKEN_KEY_DOWN 27
1674 #define SETUP_TOKEN_KEY_SNAP 28
1675 #define SETUP_TOKEN_KEY_BOMB 29
1677 /* level directory info */
1678 #define LEVELINFO_TOKEN_NAME 30
1679 #define LEVELINFO_TOKEN_NAME_SHORT 31
1680 #define LEVELINFO_TOKEN_NAME_SORTING 32
1681 #define LEVELINFO_TOKEN_AUTHOR 33
1682 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1683 #define LEVELINFO_TOKEN_LEVELS 35
1684 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1685 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1686 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1687 #define LEVELINFO_TOKEN_READONLY 39
1689 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1690 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1692 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1693 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1695 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1696 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1698 #define TYPE_BOOLEAN 1
1699 #define TYPE_SWITCH 2
1701 #define TYPE_INTEGER 4
1702 #define TYPE_STRING 5
1704 static struct SetupInfo si;
1705 static struct SetupInputInfo sii;
1706 static struct LevelDirInfo ldi;
1715 { TYPE_STRING, &si.player_name, "player_name" },
1716 { TYPE_SWITCH, &si.sound, "sound" },
1717 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1718 { TYPE_SWITCH, &si.sound_music, "background_music" },
1719 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1722 { TYPE_SWITCH, &si.toons, "toons" },
1723 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1726 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1727 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1728 { TYPE_SWITCH, &si.fading, "screen_fading" },
1729 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1730 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1731 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1732 { TYPE_SWITCH, &si.handicap, "handicap" },
1733 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1734 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1737 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1738 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1739 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1740 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1741 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1742 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1743 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1744 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1745 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1746 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1747 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1748 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1749 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1750 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1751 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1752 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1754 /* level directory info */
1755 { TYPE_STRING, &ldi.name, "name" },
1756 { TYPE_STRING, &ldi.name_short, "name_short" },
1757 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1758 { TYPE_STRING, &ldi.author, "author" },
1759 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1760 { TYPE_INTEGER, &ldi.levels, "levels" },
1761 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1762 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1763 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1764 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1767 static char *string_tolower(char *s)
1769 static char s_lower[100];
1772 if (strlen(s) >= 100)
1777 for (i=0; i<strlen(s_lower); i++)
1778 s_lower[i] = tolower(s_lower[i]);
1783 static int get_string_integer_value(char *s)
1785 static char *number_text[][3] =
1787 { "0", "zero", "null", },
1788 { "1", "one", "first" },
1789 { "2", "two", "second" },
1790 { "3", "three", "third" },
1791 { "4", "four", "fourth" },
1792 { "5", "five", "fifth" },
1793 { "6", "six", "sixth" },
1794 { "7", "seven", "seventh" },
1795 { "8", "eight", "eighth" },
1796 { "9", "nine", "ninth" },
1797 { "10", "ten", "tenth" },
1798 { "11", "eleven", "eleventh" },
1799 { "12", "twelve", "twelfth" },
1804 for (i=0; i<13; i++)
1806 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1812 static boolean get_string_boolean_value(char *s)
1814 if (strcmp(string_tolower(s), "true") == 0 ||
1815 strcmp(string_tolower(s), "yes") == 0 ||
1816 strcmp(string_tolower(s), "on") == 0 ||
1817 get_string_integer_value(s) == 1)
1823 static char *getFormattedSetupEntry(char *token, char *value)
1826 static char entry[MAX_LINE_LEN];
1828 sprintf(entry, "%s:", token);
1829 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1833 strcat(entry, value);
1838 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1840 if (!setup_file_list)
1843 if (setup_file_list->token)
1844 free(setup_file_list->token);
1845 if (setup_file_list->value)
1846 free(setup_file_list->value);
1847 if (setup_file_list->next)
1848 freeSetupFileList(setup_file_list->next);
1849 free(setup_file_list);
1852 static struct SetupFileList *newSetupFileList(char *token, char *value)
1854 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1856 new->token = checked_malloc(strlen(token) + 1);
1857 strcpy(new->token, token);
1859 new->value = checked_malloc(strlen(value) + 1);
1860 strcpy(new->value, value);
1867 static char *getTokenValue(struct SetupFileList *setup_file_list,
1870 if (!setup_file_list)
1873 if (strcmp(setup_file_list->token, token) == 0)
1874 return setup_file_list->value;
1876 return getTokenValue(setup_file_list->next, token);
1879 static void setTokenValue(struct SetupFileList *setup_file_list,
1880 char *token, char *value)
1882 if (!setup_file_list)
1885 if (strcmp(setup_file_list->token, token) == 0)
1887 free(setup_file_list->value);
1888 setup_file_list->value = checked_malloc(strlen(value) + 1);
1889 strcpy(setup_file_list->value, value);
1891 else if (setup_file_list->next == NULL)
1892 setup_file_list->next = newSetupFileList(token, value);
1894 setTokenValue(setup_file_list->next, token, value);
1898 static void printSetupFileList(struct SetupFileList *setup_file_list)
1900 if (!setup_file_list)
1903 printf("token: '%s'\n", setup_file_list->token);
1904 printf("value: '%s'\n", setup_file_list->value);
1906 printSetupFileList(setup_file_list->next);
1910 static struct SetupFileList *loadSetupFileList(char *filename)
1913 char line[MAX_LINE_LEN];
1914 char *token, *value, *line_ptr;
1915 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1916 struct SetupFileList *first_valid_list_entry;
1920 if (!(file = fopen(filename, MODE_READ)))
1922 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1928 /* read next line of input file */
1929 if (!fgets(line, MAX_LINE_LEN, file))
1932 /* cut trailing comment or whitespace from input line */
1933 for (line_ptr = line; *line_ptr; line_ptr++)
1935 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1942 /* cut trailing whitespaces from input line */
1943 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1944 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1947 /* ignore empty lines */
1951 line_len = strlen(line);
1953 /* cut leading whitespaces from token */
1954 for (token = line; *token; token++)
1955 if (*token != ' ' && *token != '\t')
1958 /* find end of token */
1959 for (line_ptr = token; *line_ptr; line_ptr++)
1961 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1968 if (line_ptr < line + line_len)
1969 value = line_ptr + 1;
1973 /* cut leading whitespaces from value */
1974 for (; *value; value++)
1975 if (*value != ' ' && *value != '\t')
1978 if (*token && *value)
1979 setTokenValue(setup_file_list, token, value);
1984 first_valid_list_entry = setup_file_list->next;
1986 /* free empty list header */
1987 setup_file_list->next = NULL;
1988 freeSetupFileList(setup_file_list);
1990 if (first_valid_list_entry == NULL)
1991 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1993 return first_valid_list_entry;
1996 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1999 if (!setup_file_list)
2002 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2004 if (strcmp(setup_file_list->value, identifier) != 0)
2006 Error(ERR_WARN, "configuration file has wrong version");
2013 if (setup_file_list->next)
2014 checkSetupFileListIdentifier(setup_file_list->next, identifier);
2017 Error(ERR_WARN, "configuration file has no version information");
2022 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2024 ldi->filename = NULL;
2025 ldi->fullpath = NULL;
2026 ldi->basepath = NULL;
2027 ldi->name = getStringCopy(ANONYMOUS_NAME);
2028 ldi->name_short = NULL;
2029 ldi->name_sorting = NULL;
2030 ldi->author = getStringCopy(ANONYMOUS_NAME);
2031 ldi->imported_from = NULL;
2033 ldi->first_level = 0;
2034 ldi->last_level = 0;
2035 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
2036 ldi->level_group = FALSE;
2037 ldi->parent_link = FALSE;
2038 ldi->user_defined = FALSE;
2039 ldi->readonly = TRUE;
2041 ldi->class_desc = NULL;
2042 ldi->handicap_level = 0;
2044 ldi->cl_cursor = -1;
2046 ldi->node_parent = NULL;
2047 ldi->node_group = NULL;
2051 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2052 struct LevelDirInfo *parent)
2056 setLevelDirInfoToDefaults(ldi);
2060 /* first copy all values from the parent structure ... */
2063 /* ... then set all fields to default that cannot be inherited from parent.
2064 This is especially important for all those fields that can be set from
2065 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2066 calls 'free()' for all already set token values which requires that no
2067 other structure's pointer may point to them!
2070 ldi->filename = NULL;
2071 ldi->fullpath = NULL;
2072 ldi->basepath = NULL;
2073 ldi->name = getStringCopy(ANONYMOUS_NAME);
2074 ldi->name_short = NULL;
2075 ldi->name_sorting = NULL;
2076 ldi->author = getStringCopy(parent->author);
2077 ldi->imported_from = getStringCopy(parent->imported_from);
2079 ldi->level_group = FALSE;
2080 ldi->parent_link = FALSE;
2082 ldi->node_parent = parent;
2083 ldi->node_group = NULL;
2087 static void setSetupInfoToDefaults(struct SetupInfo *si)
2091 si->player_name = getStringCopy(getLoginName());
2094 si->sound_loops = TRUE;
2095 si->sound_music = TRUE;
2096 si->sound_simple = TRUE;
2098 si->double_buffering = TRUE;
2099 si->direct_draw = !si->double_buffering;
2100 si->scroll_delay = TRUE;
2101 si->soft_scrolling = TRUE;
2103 si->autorecord = TRUE;
2104 si->quick_doors = FALSE;
2105 si->team_mode = FALSE;
2106 si->handicap = TRUE;
2107 si->time_limit = TRUE;
2108 si->fullscreen = FALSE;
2110 for (i=0; i<MAX_PLAYERS; i++)
2112 si->input[i].use_joystick = FALSE;
2113 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2114 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2115 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2116 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2117 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2118 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2119 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2120 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2121 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2122 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2123 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2124 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2125 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2126 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2127 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2131 static void setSetupInfo(int token_nr, char *token_value)
2133 int token_type = token_info[token_nr].type;
2134 void *setup_value = token_info[token_nr].value;
2136 if (token_value == NULL)
2139 /* set setup field to corresponding token value */
2144 *(boolean *)setup_value = get_string_boolean_value(token_value);
2148 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2152 *(int *)setup_value = get_string_integer_value(token_value);
2156 if (*(char **)setup_value != NULL)
2157 free(*(char **)setup_value);
2158 *(char **)setup_value = getStringCopy(token_value);
2166 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2170 if (!setup_file_list)
2173 /* handle global setup values */
2175 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2176 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2179 /* handle player specific setup values */
2180 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2184 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2186 sii = setup.input[pnr];
2187 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2189 char full_token[100];
2191 sprintf(full_token, "%s%s", prefix, token_info[i].text);
2192 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2194 setup.input[pnr] = sii;
2198 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2200 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2201 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2204 if (entry1->parent_link || entry2->parent_link)
2205 compare_result = (entry1->parent_link ? -1 : +1);
2206 else if (entry1->sort_priority == entry2->sort_priority)
2208 char *name1 = getStringToLower(entry1->name_sorting);
2209 char *name2 = getStringToLower(entry2->name_sorting);
2211 compare_result = strcmp(name1, name2);
2216 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2217 compare_result = entry1->sort_priority - entry2->sort_priority;
2219 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2221 return compare_result;
2224 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2226 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2228 setLevelDirInfoToDefaults(leveldir_new);
2230 leveldir_new->node_parent = node_parent;
2231 leveldir_new->parent_link = TRUE;
2233 leveldir_new->name = ".. (parent directory)";
2234 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2235 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2237 leveldir_new->filename = "..";
2238 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2240 leveldir_new->sort_priority = node_parent->sort_priority;
2241 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2243 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2246 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2247 struct LevelDirInfo *node_parent,
2248 char *level_directory)
2251 struct dirent *dir_entry;
2252 boolean valid_entry_found = FALSE;
2254 if ((dir = opendir(level_directory)) == NULL)
2256 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2260 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2262 struct SetupFileList *setup_file_list = NULL;
2263 struct stat file_status;
2264 char *directory_name = dir_entry->d_name;
2265 char *directory_path = getPath2(level_directory, directory_name);
2266 char *filename = NULL;
2268 /* skip entries for current and parent directory */
2269 if (strcmp(directory_name, ".") == 0 ||
2270 strcmp(directory_name, "..") == 0)
2272 free(directory_path);
2276 /* find out if directory entry is itself a directory */
2277 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2278 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2280 free(directory_path);
2284 filename = getPath2(directory_path, LEVELINFO_FILENAME);
2285 setup_file_list = loadSetupFileList(filename);
2287 if (setup_file_list)
2289 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2292 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2293 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2295 /* set all structure fields according to the token/value pairs */
2296 ldi = *leveldir_new;
2297 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2298 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2299 *leveldir_new = ldi;
2301 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2303 if (leveldir_new->name_short == NULL)
2304 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2306 if (leveldir_new->name_sorting == NULL)
2307 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2309 leveldir_new->filename = getStringCopy(directory_name);
2311 if (node_parent == NULL) /* top level group */
2313 leveldir_new->basepath = level_directory;
2314 leveldir_new->fullpath = leveldir_new->filename;
2316 else /* sub level group */
2318 leveldir_new->basepath = node_parent->basepath;
2319 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2323 if (leveldir_new->levels < 1)
2324 leveldir_new->levels = 1;
2326 leveldir_new->last_level =
2327 leveldir_new->first_level + leveldir_new->levels - 1;
2329 leveldir_new->user_defined =
2330 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2332 leveldir_new->color = LEVELCOLOR(leveldir_new);
2333 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2335 leveldir_new->handicap_level = /* set handicap to default value */
2336 (leveldir_new->user_defined ?
2337 leveldir_new->last_level :
2338 leveldir_new->first_level);
2340 pushLevelDirInfo(node_first, leveldir_new);
2342 freeSetupFileList(setup_file_list);
2343 valid_entry_found = TRUE;
2345 if (leveldir_new->level_group)
2347 /* create node to link back to current level directory */
2348 createParentLevelDirNode(leveldir_new);
2350 /* step into sub-directory and look for more level series */
2351 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2352 leveldir_new, directory_path);
2356 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2358 free(directory_path);
2364 if (!valid_entry_found)
2365 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2369 void LoadLevelInfo()
2371 InitUserLevelDirectory(getLoginName());
2373 DrawInitText("Loading level series:", 120, FC_GREEN);
2375 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2376 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2378 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2380 if (leveldir_first == NULL)
2381 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2383 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2386 dumpLevelDirInfo(leveldir_first, 0);
2390 static void SaveUserLevelInfo()
2396 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2398 if (!(file = fopen(filename, MODE_WRITE)))
2400 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2405 /* always start with reliable default values */
2406 setLevelDirInfoToDefaults(&ldi);
2408 ldi.name = getLoginName();
2409 ldi.author = getRealName();
2411 ldi.first_level = 1;
2412 ldi.sort_priority = LEVELCLASS_USER_START;
2413 ldi.readonly = FALSE;
2415 fprintf(file, "%s\n\n",
2416 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2418 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2419 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2420 i != LEVELINFO_TOKEN_NAME_SORTING &&
2421 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2422 fprintf(file, "%s\n", getSetupLine("", i));
2427 chmod(filename, SETUP_PERMS);
2433 struct SetupFileList *setup_file_list = NULL;
2435 /* always start with reliable default values */
2436 setSetupInfoToDefaults(&setup);
2438 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2440 setup_file_list = loadSetupFileList(filename);
2442 if (setup_file_list)
2444 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2445 decodeSetupFileList(setup_file_list);
2447 setup.direct_draw = !setup.double_buffering;
2449 freeSetupFileList(setup_file_list);
2451 /* needed to work around problems with fixed length strings */
2452 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2453 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2454 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2456 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2458 strcpy(new_name, setup.player_name);
2459 free(setup.player_name);
2460 setup.player_name = new_name;
2464 Error(ERR_WARN, "using default setup values");
2469 static char *getSetupLine(char *prefix, int token_nr)
2472 static char entry[MAX_LINE_LEN];
2473 int token_type = token_info[token_nr].type;
2474 void *setup_value = token_info[token_nr].value;
2475 char *token_text = token_info[token_nr].text;
2477 /* start with the prefix, token and some spaces to format output line */
2478 sprintf(entry, "%s%s:", prefix, token_text);
2479 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2482 /* continue with the token's value (which can have different types) */
2486 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2490 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2495 Key key = *(Key *)setup_value;
2496 char *keyname = getKeyNameFromKey(key);
2498 strcat(entry, getX11KeyNameFromKey(key));
2499 for (i=strlen(entry); i<50; i++)
2502 /* add comment, if useful */
2503 if (strcmp(keyname, "(undefined)") != 0 &&
2504 strcmp(keyname, "(unknown)") != 0)
2506 strcat(entry, "# ");
2507 strcat(entry, keyname);
2514 char buffer[MAX_LINE_LEN];
2516 sprintf(buffer, "%d", *(int *)setup_value);
2517 strcat(entry, buffer);
2522 strcat(entry, *(char **)setup_value);
2538 InitUserDataDirectory();
2540 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2542 if (!(file = fopen(filename, MODE_WRITE)))
2544 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2549 fprintf(file, "%s\n",
2550 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2551 fprintf(file, "\n");
2553 /* handle global setup values */
2555 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2557 fprintf(file, "%s\n", getSetupLine("", i));
2559 /* just to make things nicer :) */
2560 if (i == SETUP_TOKEN_PLAYER_NAME)
2561 fprintf(file, "\n");
2564 /* handle player specific setup values */
2565 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2569 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2570 fprintf(file, "\n");
2572 sii = setup.input[pnr];
2573 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2574 fprintf(file, "%s\n", getSetupLine(prefix, i));
2580 chmod(filename, SETUP_PERMS);
2583 void LoadLevelSetup_LastSeries()
2586 struct SetupFileList *level_setup_list = NULL;
2588 /* always start with reliable default values */
2589 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2591 /* ----------------------------------------------------------------------- */
2592 /* ~/.rocksndiamonds/levelsetup.conf */
2593 /* ----------------------------------------------------------------------- */
2595 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2597 if ((level_setup_list = loadSetupFileList(filename)))
2599 char *last_level_series =
2600 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2602 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2603 if (leveldir_current == NULL)
2604 leveldir_current = leveldir_first;
2606 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2608 freeSetupFileList(level_setup_list);
2611 Error(ERR_WARN, "using default setup values");
2616 void SaveLevelSetup_LastSeries()
2619 char *level_subdir = leveldir_current->filename;
2622 /* ----------------------------------------------------------------------- */
2623 /* ~/.rocksndiamonds/levelsetup.conf */
2624 /* ----------------------------------------------------------------------- */
2626 InitUserDataDirectory();
2628 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2630 if (!(file = fopen(filename, MODE_WRITE)))
2632 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2637 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2638 LEVELSETUP_COOKIE));
2639 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2645 chmod(filename, SETUP_PERMS);
2648 static void checkSeriesInfo()
2650 static char *level_directory = NULL;
2652 struct dirent *dir_entry;
2654 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2656 level_directory = getPath2((leveldir_current->user_defined ?
2657 getUserLevelDir("") :
2658 options.level_directory),
2659 leveldir_current->fullpath);
2661 if ((dir = opendir(level_directory)) == NULL)
2663 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2667 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2669 if (strlen(dir_entry->d_name) > 4 &&
2670 dir_entry->d_name[3] == '.' &&
2671 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2673 char levelnum_str[4];
2676 strncpy(levelnum_str, dir_entry->d_name, 3);
2677 levelnum_str[3] = '\0';
2679 levelnum_value = atoi(levelnum_str);
2681 if (levelnum_value < leveldir_current->first_level)
2683 Error(ERR_WARN, "additional level %d found", levelnum_value);
2684 leveldir_current->first_level = levelnum_value;
2686 else if (levelnum_value > leveldir_current->last_level)
2688 Error(ERR_WARN, "additional level %d found", levelnum_value);
2689 leveldir_current->last_level = levelnum_value;
2697 void LoadLevelSetup_SeriesInfo()
2700 struct SetupFileList *level_setup_list = NULL;
2701 char *level_subdir = leveldir_current->filename;
2703 /* always start with reliable default values */
2704 level_nr = leveldir_current->first_level;
2706 checkSeriesInfo(leveldir_current);
2708 /* ----------------------------------------------------------------------- */
2709 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2710 /* ----------------------------------------------------------------------- */
2712 level_subdir = leveldir_current->filename;
2714 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2716 if ((level_setup_list = loadSetupFileList(filename)))
2720 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2724 level_nr = atoi(token_value);
2726 if (level_nr < leveldir_current->first_level)
2727 level_nr = leveldir_current->first_level;
2728 if (level_nr > leveldir_current->last_level)
2729 level_nr = leveldir_current->last_level;
2732 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2736 int level_nr = atoi(token_value);
2738 if (level_nr < leveldir_current->first_level)
2739 level_nr = leveldir_current->first_level;
2740 if (level_nr > leveldir_current->last_level + 1)
2741 level_nr = leveldir_current->last_level;
2743 if (leveldir_current->user_defined)
2744 level_nr = leveldir_current->last_level;
2746 leveldir_current->handicap_level = level_nr;
2749 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2751 freeSetupFileList(level_setup_list);
2754 Error(ERR_WARN, "using default setup values");
2759 void SaveLevelSetup_SeriesInfo()
2762 char *level_subdir = leveldir_current->filename;
2763 char *level_nr_str = int2str(level_nr, 0);
2764 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2767 /* ----------------------------------------------------------------------- */
2768 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2769 /* ----------------------------------------------------------------------- */
2771 InitLevelSetupDirectory(level_subdir);
2773 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2775 if (!(file = fopen(filename, MODE_WRITE)))
2777 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2782 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2783 LEVELSETUP_COOKIE));
2784 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2786 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2787 handicap_level_str));
2792 chmod(filename, SETUP_PERMS);
2794 /* LocalWords: Rocks'n