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 = FALSE; /* default: only 8-bit elements */
414 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
415 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
417 for(x=0; x<MAX_LEV_FIELDX; x++)
418 for(y=0; y<MAX_LEV_FIELDY; y++)
419 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
422 level.gems_needed = 0;
423 level.amoeba_speed = 10;
424 level.time_magic_wall = 10;
425 level.time_wheel = 10;
426 level.time_light = 10;
427 level.time_timegate = 10;
428 level.amoeba_content = EL_DIAMANT;
429 level.double_speed = FALSE;
430 level.gravity = FALSE;
432 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
433 level.name[i] = '\0';
434 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
435 level.author[i] = '\0';
437 strcpy(level.name, NAMELESS_LEVEL_NAME);
438 strcpy(level.author, ANONYMOUS_NAME);
440 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
443 level.num_yam_contents = STD_ELEMENT_CONTENTS;
444 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
447 level.yam_content[i][x][y] = EL_FELSBROCKEN;
449 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
450 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
451 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
453 BorderElement = EL_BETON;
455 /* try to determine better author name than 'anonymous' */
456 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
458 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
459 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
463 switch (LEVELCLASS(leveldir_current))
465 case LEVELCLASS_TUTORIAL:
466 strcpy(level.author, PROGRAM_AUTHOR_STRING);
469 case LEVELCLASS_CONTRIBUTION:
470 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
471 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
474 case LEVELCLASS_USER:
475 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
476 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
480 /* keep default value */
486 static int checkLevelElement(int element)
488 if (element >= EL_FIRST_RUNTIME_EL)
490 Error(ERR_WARN, "invalid level element %d", element);
491 element = EL_CHAR_FRAGE;
497 void OLD_LoadLevel(int level_nr)
500 char *filename = getLevelFilename(level_nr);
501 char cookie[MAX_LINE_LEN];
502 char chunk_name[CHUNK_ID_LEN + 1];
503 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
504 int file_version = FILE_VERSION_ACTUAL;
508 /* always start with reliable default values */
509 setLevelInfoToDefaults();
511 if (!(file = fopen(filename, MODE_READ)))
513 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
517 /* check file identifier */
518 fgets(cookie, MAX_LINE_LEN, file);
519 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
520 cookie[strlen(cookie) - 1] = '\0';
523 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
524 file_version = FILE_VERSION_1_0;
525 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
526 file_version = FILE_VERSION_1_2;
527 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
529 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
534 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
536 Error(ERR_WARN, "unknown format of level file '%s'", filename);
541 file_version = getFileVersionFromCookieString(cookie);
544 level.file_version = file_version;
546 /* read chunk "HEAD" */
547 if (file_version >= FILE_VERSION_1_2)
549 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
550 if (strcmp(chunk_name, "HEAD") || chunk_size != LEVEL_HEADER_SIZE)
552 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
558 lev_fieldx = level.fieldx = fgetc(file);
559 lev_fieldy = level.fieldy = fgetc(file);
561 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
562 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
564 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
565 level.name[i] = fgetc(file);
566 level.name[MAX_LEVEL_NAME_LEN] = 0;
568 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
569 level.score[i] = fgetc(file);
571 level.num_yam_contents = STD_ELEMENT_CONTENTS;
572 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
578 if (i < STD_ELEMENT_CONTENTS)
579 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
581 level.yam_content[i][x][y] = EL_LEERRAUM;
586 level.amoeba_speed = fgetc(file);
587 level.time_magic_wall = fgetc(file);
588 level.time_wheel = fgetc(file);
589 level.amoeba_content = checkLevelElement(fgetc(file));
590 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
591 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
593 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
595 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
598 if (file_version >= FILE_VERSION_1_2)
600 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
602 /* look for optional author chunk */
603 if (strcmp(chunk_name, "AUTH") == 0 && chunk_size == MAX_LEVEL_AUTHOR_LEN)
605 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
606 level.author[i] = fgetc(file);
607 level.author[MAX_LEVEL_NAME_LEN] = 0;
609 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
612 /* look for optional content chunk */
613 if (strcmp(chunk_name, "CONT") == 0 &&
614 chunk_size == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
617 level.num_yam_contents = fgetc(file);
621 if (level.num_yam_contents < 1 ||
622 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
625 printf("WARNING: num_yam_contents == %d (corrected)\n",
626 level.num_yam_contents);
628 level.num_yam_contents = STD_ELEMENT_CONTENTS;
631 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
634 level.yam_content[i][x][y] =
635 checkLevelElement(encoding_16bit ?
636 getFile16BitInteger(file,
637 BYTE_ORDER_BIG_ENDIAN) :
640 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
643 /* next check body chunk identifier and chunk size */
644 if (strcmp(chunk_name, "BODY") != 0 ||
645 chunk_size != lev_fieldx * lev_fieldy)
647 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
653 /* clear all other level fields (needed if resized in level editor later) */
654 for(x=0; x<MAX_LEV_FIELDX; x++)
655 for(y=0; y<MAX_LEV_FIELDY; y++)
656 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
658 /* now read in the valid level fields from level file */
659 for(y=0; y<lev_fieldy; y++)
660 for(x=0; x<lev_fieldx; x++)
661 Feld[x][y] = Ur[x][y] =
662 checkLevelElement(encoding_16bit ?
663 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
668 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
669 IS_LEVELCLASS_USER(leveldir_current))
671 /* for user contributed and private levels, use the version of
672 the game engine the levels were created for */
673 level.game_version = file_version;
675 /* player was faster than monsters in pre-1.0 levels */
676 if (file_version == FILE_VERSION_1_0)
678 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
679 Error(ERR_WARN, "using high speed movement for player");
680 level.double_speed = TRUE;
685 /* always use the latest version of the game engine for all but
686 user contributed and private levels */
687 level.game_version = GAME_VERSION_ACTUAL;
690 /* determine border element for this level */
694 static void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
700 static void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
706 static int LoadLevel_HEAD(struct LevelInfo *level, FILE *file, int chunk_size)
710 lev_fieldx = level->fieldx = fgetc(file);
711 lev_fieldy = level->fieldy = fgetc(file);
713 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
714 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
716 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
717 level->name[i] = fgetc(file);
718 level->name[MAX_LEVEL_NAME_LEN] = 0;
720 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
721 level->score[i] = fgetc(file);
723 level->num_yam_contents = STD_ELEMENT_CONTENTS;
724 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
730 if (i < STD_ELEMENT_CONTENTS)
731 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
733 level->yam_content[i][x][y] = EL_LEERRAUM;
738 level->amoeba_speed = fgetc(file);
739 level->time_magic_wall = fgetc(file);
740 level->time_wheel = fgetc(file);
741 level->amoeba_content = checkLevelElement(fgetc(file));
742 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
743 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
745 level->encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
747 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
752 static int LoadLevel_AUTH(struct LevelInfo *level, FILE *file, int chunk_size)
756 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
757 level->author[i] = fgetc(file);
758 level->author[MAX_LEVEL_NAME_LEN] = 0;
763 static int LoadLevel_CONT(struct LevelInfo *level, FILE *file, int chunk_size)
767 int content_size_type1 = MAX_ELEMENT_CONTENTS * 3 * 3;
769 int chunk_size_expected = header_size + content_size_type1;
771 /* Note: "chunk_size" was wrong before version 2.0 when elements are
772 stored with 16-bit encoding (and should be twice as big then). */
773 if (level->encoding_16bit && level->file_version >= FILE_VERSION_2_0)
774 chunk_size_expected += content_size_type1;
776 if (chunk_size_expected != chunk_size)
778 ReadUnusedBytesFromFile(file, chunk_size);
779 return chunk_size_expected;
783 level->num_yam_contents = fgetc(file);
787 /* correct invalid number of content fields -- should never happen */
788 if (level->num_yam_contents < 1 ||
789 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
790 level->num_yam_contents = STD_ELEMENT_CONTENTS;
792 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
795 level->yam_content[i][x][y] =
796 checkLevelElement(level->encoding_16bit ?
797 getFile16BitInteger(file,
798 BYTE_ORDER_BIG_ENDIAN) :
803 static int LoadLevel_BODY(struct LevelInfo *level, FILE *file, int chunk_size)
806 int chunk_size_expected = level->fieldx * level->fieldy;
808 /* Note: "chunk_size" was wrong before version 2.0 when elements are
809 stored with 16-bit encoding (and should be twice as big then). */
810 if (level->encoding_16bit && level->file_version >= FILE_VERSION_2_0)
811 chunk_size_expected *= 2;
813 if (chunk_size_expected != chunk_size)
815 ReadUnusedBytesFromFile(file, chunk_size);
816 return chunk_size_expected;
819 for(y=0; y<level->fieldy; y++)
820 for(x=0; x<level->fieldx; x++)
821 Feld[x][y] = Ur[x][y] =
822 checkLevelElement(level->encoding_16bit ?
823 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
828 static int LoadLevel_CNT2(struct LevelInfo *level, FILE *file, int chunk_size)
832 int num_contents, content_xsize, content_ysize;
833 int content_array[MAX_ELEMENT_CONTENTS][3][3];
835 element = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
836 num_contents = fgetc(file);
837 content_xsize = fgetc(file);
838 content_ysize = fgetc(file);
839 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
841 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
844 content_array[i][x][y] =
845 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
847 /* correct invalid number of content fields -- should never happen */
848 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
849 num_contents = STD_ELEMENT_CONTENTS;
851 if (element == EL_MAMPFER)
853 level->num_yam_contents = num_contents;
855 for(i=0; i<num_contents; i++)
858 level->yam_content[i][x][y] = content_array[i][x][y];
860 else if (element == EL_AMOEBE_BD)
862 level->amoeba_content = content_array[0][0][0];
866 Error(ERR_WARN, "cannot load content for element '%d'", element);
872 void LoadLevel(int level_nr)
874 char *filename = getLevelFilename(level_nr);
875 char cookie[MAX_LINE_LEN];
876 char chunk_name[CHUNK_ID_LEN + 1];
880 /* always start with reliable default values */
881 setLevelInfoToDefaults();
883 if (!(file = fopen(filename, MODE_READ)))
885 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
889 /* check file identifier */
890 fgets(cookie, MAX_LINE_LEN, file);
891 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
892 cookie[strlen(cookie) - 1] = '\0';
894 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
896 Error(ERR_WARN, "unknown format of level file '%s'", filename);
901 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
903 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
908 if (level.file_version < FILE_VERSION_1_2)
910 /* level files from versions before 1.2.0 without chunk structure */
911 LoadLevel_HEAD(&level, file, LEVEL_HEADER_SIZE);
912 LoadLevel_BODY(&level, file, level.fieldx * level.fieldy);
920 int (*loader)(struct LevelInfo *, FILE *, int);
924 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
925 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
926 { "CONT", -1, LoadLevel_CONT },
927 { "BODY", -1, LoadLevel_BODY },
928 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
932 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
936 while (chunk_info[i].name != NULL &&
937 strcmp(chunk_name, chunk_info[i].name) != 0)
940 if (chunk_info[i].name == NULL)
942 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
943 chunk_name, filename);
944 ReadUnusedBytesFromFile(file, chunk_size);
946 else if (chunk_info[i].size != -1 &&
947 chunk_info[i].size != chunk_size)
949 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
950 chunk_size, chunk_name, filename);
951 ReadUnusedBytesFromFile(file, chunk_size);
955 /* call function to load this level chunk */
956 int chunk_size_expected =
957 (chunk_info[i].loader)(&level, file, chunk_size);
959 /* the size of some chunks cannot be checked before reading other
960 chunks first (like "HEAD" and "BODY") or before reading some
961 header information first (like "CONT"), so check them here */
962 if (chunk_size_expected != chunk_size)
964 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
965 chunk_size, chunk_name, filename);
973 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
974 IS_LEVELCLASS_USER(leveldir_current))
976 /* for user contributed and private levels, use the version of
977 the game engine the levels were created for */
978 level.game_version = level.file_version;
980 /* player was faster than monsters in pre-1.0 levels */
981 if (level.file_version == FILE_VERSION_1_0)
983 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
984 Error(ERR_WARN, "using high speed movement for player");
985 level.double_speed = TRUE;
990 /* always use the latest version of the game engine for all but
991 user contributed and private levels */
992 level.game_version = GAME_VERSION_ACTUAL;
995 /* determine border element for this level */
999 void OLD_SaveLevel(int level_nr)
1002 char *filename = getLevelFilename(level_nr);
1004 boolean encoding_16bit_amoeba = FALSE;
1005 boolean encoding_16bit_yamyam = FALSE;
1007 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
1008 char *oldest_possible_cookie;
1011 if (!(file = fopen(filename, MODE_WRITE)))
1013 Error(ERR_WARN, "cannot save level file '%s'", filename);
1017 /* check yam content for 16-bit elements */
1018 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1021 if (level.yam_content[i][x][y] > 255)
1022 encoding_16bit = TRUE;
1024 /* check level field for 16-bit elements */
1025 for(y=0; y<lev_fieldy; y++)
1026 for(x=0; x<lev_fieldx; x++)
1028 encoding_16bit = TRUE;
1030 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
1032 fputs(oldest_possible_cookie, file); /* file identifier */
1035 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1037 fputc(level.fieldx, file);
1038 fputc(level.fieldy, file);
1040 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1041 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1043 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1044 fputc(level.name[i], file);
1045 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1046 fputc(level.score[i], file);
1047 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1050 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1051 fputc(level.amoeba_speed, file);
1052 fputc(level.time_magic_wall, file);
1053 fputc(level.time_wheel, file);
1054 fputc(level.amoeba_content, file);
1055 fputc((level.double_speed ? 1 : 0), file);
1056 fputc((level.gravity ? 1 : 0), file);
1058 fputc((encoding_16bit ? 1 : 0), file);
1060 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1063 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1065 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1066 fputc(level.author[i], file);
1068 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1069 BYTE_ORDER_BIG_ENDIAN);
1071 fputc(EL_MAMPFER, file);
1072 fputc(level.num_yam_contents, file);
1076 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1080 putFile16BitInteger(file, level.yam_content[i][x][y],
1081 BYTE_ORDER_BIG_ENDIAN);
1083 fputc(level.yam_content[i][x][y], file);
1085 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1087 for(y=0; y<lev_fieldy; y++)
1088 for(x=0; x<lev_fieldx; x++)
1090 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1092 fputc(Ur[x][y], file);
1096 chmod(filename, LEVEL_PERMS);
1099 static void SaveLevel_HEAD(struct LevelInfo *level, FILE *file)
1103 fputc(level->fieldx, file);
1104 fputc(level->fieldy, file);
1106 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
1107 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
1109 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1110 fputc(level->name[i], file);
1112 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1113 fputc(level->score[i], file);
1115 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1118 fputc((level->encoding_16bit ? EL_LEERRAUM :
1119 level->yam_content[i][x][y]),
1121 fputc(level->amoeba_speed, file);
1122 fputc(level->time_magic_wall, file);
1123 fputc(level->time_wheel, file);
1124 fputc(level->amoeba_content, file);
1125 fputc((level->double_speed ? 1 : 0), file);
1126 fputc((level->gravity ? 1 : 0), file);
1128 fputc((level->encoding_16bit ? 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);
1141 static void SaveLevel_CONT(struct LevelInfo *level, FILE *file)
1145 fputc(EL_MAMPFER, file);
1146 fputc(level->num_yam_contents, file);
1150 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1153 if (level->encoding_16bit)
1154 putFile16BitInteger(file, level->yam_content[i][x][y],
1155 BYTE_ORDER_BIG_ENDIAN);
1157 fputc(level->yam_content[i][x][y], file);
1160 static void SaveLevel_BODY(struct LevelInfo *level, FILE *file)
1164 for(y=0; y<lev_fieldy; y++)
1165 for(x=0; x<lev_fieldx; x++)
1166 if (level->encoding_16bit)
1167 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1169 fputc(Ur[x][y], file);
1172 static void SaveLevel_CNT2(struct LevelInfo *level, FILE *file, int element)
1175 int num_contents, content_xsize, content_ysize;
1176 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1178 if (element == EL_MAMPFER)
1180 num_contents = level->num_yam_contents;
1184 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1187 content_array[i][x][y] = level->yam_content[i][x][y];
1189 else if (element == EL_AMOEBE_BD)
1195 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1198 content_array[i][x][y] = EL_LEERRAUM;
1199 content_array[0][0][0] = level->amoeba_content;
1203 /* chunk header already written -- write empty chunk data */
1204 for(i=0; i<LEVEL_CHUNK_CNT2_SIZE; i++)
1207 Error(ERR_WARN, "cannot save content for element '%d'", element);
1211 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
1212 fputc(num_contents, file);
1213 fputc(content_xsize, file);
1214 fputc(content_ysize, file);
1215 for(i=0; i<LEVEL_CHUNK_CNT2_UNUSED; i++)
1218 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1221 putFile16BitInteger(file, content_array[i][x][y],
1222 BYTE_ORDER_BIG_ENDIAN);
1225 void SaveLevel(int level_nr)
1228 char *filename = getLevelFilename(level_nr);
1230 boolean encoding_16bit_amoeba = FALSE;
1231 boolean encoding_16bit_yamyam = FALSE;
1234 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
1235 char *oldest_possible_cookie;
1240 if (!(file = fopen(filename, MODE_WRITE)))
1242 Error(ERR_WARN, "cannot save level file '%s'", filename);
1246 /* check level field for 16-bit elements */
1247 for(y=0; y<lev_fieldy; y++)
1248 for(x=0; x<lev_fieldx; x++)
1250 level.encoding_16bit = TRUE;
1252 /* check yam content for 16-bit elements */
1253 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1256 if (level.yam_content[i][x][y] > 255)
1257 encoding_16bit_yamyam = TRUE;
1259 /* check amoeba content for 16-bit elements */
1260 if (level.amoeba_content > 255)
1261 encoding_16bit_amoeba = TRUE;
1263 fputs(LEVEL_COOKIE, file); /* file identifier */
1266 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1267 SaveLevel_HEAD(&level, file);
1269 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1270 SaveLevel_AUTH(&level, file);
1272 if (0 && level.encoding_16bit) /* obsolete since new "CNT2" chunk */
1274 chunk_size = 4 + 2 * (MAX_ELEMENT_CONTENTS * 3 * 3);
1276 putFileChunk(file, "CONT", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1277 SaveLevel_CONT(&level, file);
1280 chunk_size = level.fieldx * level.fieldy * (level.encoding_16bit ? 2 : 1);
1281 putFileChunk(file, "BODY", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1282 SaveLevel_BODY(&level, file);
1284 if (encoding_16bit_yamyam)
1286 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1287 SaveLevel_CNT2(&level, file, EL_MAMPFER);
1290 if (encoding_16bit_amoeba)
1292 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1293 SaveLevel_CNT2(&level, file, EL_AMOEBE_BD);
1298 chmod(filename, LEVEL_PERMS);
1301 void LoadTape(int level_nr)
1304 char *filename = getTapeFilename(level_nr);
1305 char cookie[MAX_LINE_LEN];
1306 char chunk_name[CHUNK_ID_LEN + 1];
1308 int num_participating_players;
1309 int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1312 /* always start with reliable default values (empty tape) */
1313 tape.file_version = FILE_VERSION_ACTUAL;
1314 tape.game_version = GAME_VERSION_ACTUAL;
1317 /* default values (also for pre-1.2 tapes) with only the first player */
1318 tape.player_participates[0] = TRUE;
1319 for(i=1; i<MAX_PLAYERS; i++)
1320 tape.player_participates[i] = FALSE;
1322 /* at least one (default: the first) player participates in every tape */
1323 num_participating_players = 1;
1325 if (!(file = fopen(filename, MODE_READ)))
1328 /* check file identifier */
1329 fgets(cookie, MAX_LINE_LEN, file);
1330 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1331 cookie[strlen(cookie) - 1] = '\0';
1334 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
1335 file_version = FILE_VERSION_1_0;
1336 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
1338 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1343 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1345 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1350 file_version = getFileVersionFromCookieString(cookie);
1353 tape.file_version = file_version;
1354 tape.game_version = file_version;
1356 /* read chunk "HEAD" */
1357 if (file_version >= FILE_VERSION_1_2)
1359 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1360 if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1362 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1368 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1369 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1370 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1372 /* read header fields that are new since version 1.2 */
1373 if (file_version >= FILE_VERSION_1_2)
1375 byte store_participating_players = fgetc(file);
1377 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
1380 /* since version 1.2, tapes store which players participate in the tape */
1381 num_participating_players = 0;
1382 for(i=0; i<MAX_PLAYERS; i++)
1384 tape.player_participates[i] = FALSE;
1386 if (store_participating_players & (1 << i))
1388 tape.player_participates[i] = TRUE;
1389 num_participating_players++;
1394 tape.level_nr = level_nr;
1396 tape.changed = FALSE;
1398 tape.recording = FALSE;
1399 tape.playing = FALSE;
1400 tape.pausing = FALSE;
1402 /* read chunk "BODY" */
1403 if (file_version >= FILE_VERSION_1_2)
1405 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1406 if (strcmp(chunk_name, "BODY") ||
1407 chunk_size != (num_participating_players + 1) * tape.length)
1409 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1415 for(i=0; i<tape.length; i++)
1417 if (i >= MAX_TAPELEN)
1420 for(j=0; j<MAX_PLAYERS; j++)
1422 tape.pos[i].action[j] = MV_NO_MOVING;
1424 if (tape.player_participates[j])
1425 tape.pos[i].action[j] = fgetc(file);
1428 tape.pos[i].delay = fgetc(file);
1430 if (file_version == FILE_VERSION_1_0)
1432 /* eliminate possible diagonal moves in old tapes */
1433 /* this is only for backward compatibility */
1435 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1436 byte action = tape.pos[i].action[0];
1437 int k, num_moves = 0;
1441 if (action & joy_dir[k])
1443 tape.pos[i + num_moves].action[0] = joy_dir[k];
1445 tape.pos[i + num_moves].delay = 0;
1454 tape.length += num_moves;
1464 if (i != tape.length)
1465 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1467 tape.length_seconds = GetTapeLength();
1470 void SaveTape(int level_nr)
1473 char *filename = getTapeFilename(level_nr);
1475 boolean new_tape = TRUE;
1476 byte store_participating_players;
1477 int num_participating_players;
1479 InitTapeDirectory(leveldir_current->filename);
1481 /* if a tape still exists, ask to overwrite it */
1482 if (access(filename, F_OK) == 0)
1485 if (!Request("Replace old tape ?", REQ_ASK))
1489 /* count number of players and set corresponding bits for compact storage */
1490 store_participating_players = 0;
1491 num_participating_players = 0;
1492 for(i=0; i<MAX_PLAYERS; i++)
1494 if (tape.player_participates[i])
1496 num_participating_players++;
1497 store_participating_players |= (1 << i);
1501 if (!(file = fopen(filename, MODE_WRITE)))
1503 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1507 fputs(TAPE_COOKIE, file); /* file identifier */
1510 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1512 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1513 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1514 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1516 fputc(store_participating_players, file);
1518 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1521 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1522 BYTE_ORDER_BIG_ENDIAN);
1524 for(i=0; i<tape.length; i++)
1528 for(j=0; j<MAX_PLAYERS; j++)
1529 if (tape.player_participates[j])
1530 fputc(tape.pos[i].action[j], file);
1532 fputc(tape.pos[i].delay, file);
1537 chmod(filename, TAPE_PERMS);
1539 tape.changed = FALSE;
1542 Request("tape saved !", REQ_CONFIRM);
1545 void LoadScore(int level_nr)
1548 char *filename = getScoreFilename(level_nr);
1549 char cookie[MAX_LINE_LEN];
1550 char line[MAX_LINE_LEN];
1554 /* always start with reliable default values */
1555 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1557 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1558 highscore[i].Score = 0;
1561 if (!(file = fopen(filename, MODE_READ)))
1564 /* check file identifier */
1565 fgets(cookie, MAX_LINE_LEN, file);
1566 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1567 cookie[strlen(cookie) - 1] = '\0';
1570 if (strcmp(cookie, SCORE_COOKIE) != 0)
1572 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1577 if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1579 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1585 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1587 fscanf(file, "%d", &highscore[i].Score);
1588 fgets(line, MAX_LINE_LEN, file);
1590 if (line[strlen(line) - 1] == '\n')
1591 line[strlen(line) - 1] = '\0';
1593 for (line_ptr = line; *line_ptr; line_ptr++)
1595 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1597 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1598 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1607 void SaveScore(int level_nr)
1610 char *filename = getScoreFilename(level_nr);
1613 InitScoreDirectory(leveldir_current->filename);
1615 if (!(file = fopen(filename, MODE_WRITE)))
1617 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1621 fprintf(file, "%s\n\n", SCORE_COOKIE);
1623 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1624 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1628 chmod(filename, SCORE_PERMS);
1631 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1632 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1633 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1634 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1635 #define TOKEN_STR_PLAYER_PREFIX "player_"
1637 #define TOKEN_VALUE_POSITION 30
1640 #define SETUP_TOKEN_PLAYER_NAME 0
1641 #define SETUP_TOKEN_SOUND 1
1642 #define SETUP_TOKEN_SOUND_LOOPS 2
1643 #define SETUP_TOKEN_SOUND_MUSIC 3
1644 #define SETUP_TOKEN_SOUND_SIMPLE 4
1647 #define SETUP_TOKEN_TOONS 5
1648 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1651 #define SETUP_TOKEN_SCROLL_DELAY 5
1652 #define SETUP_TOKEN_SOFT_SCROLLING 6
1653 #define SETUP_TOKEN_FADING 7
1654 #define SETUP_TOKEN_AUTORECORD 8
1655 #define SETUP_TOKEN_QUICK_DOORS 9
1656 #define SETUP_TOKEN_TEAM_MODE 10
1657 #define SETUP_TOKEN_HANDICAP 11
1658 #define SETUP_TOKEN_TIME_LIMIT 12
1659 #define SETUP_TOKEN_FULLSCREEN 13
1662 #define SETUP_TOKEN_USE_JOYSTICK 14
1663 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1664 #define SETUP_TOKEN_JOY_XLEFT 16
1665 #define SETUP_TOKEN_JOY_XMIDDLE 17
1666 #define SETUP_TOKEN_JOY_XRIGHT 18
1667 #define SETUP_TOKEN_JOY_YUPPER 19
1668 #define SETUP_TOKEN_JOY_YMIDDLE 20
1669 #define SETUP_TOKEN_JOY_YLOWER 21
1670 #define SETUP_TOKEN_JOY_SNAP 22
1671 #define SETUP_TOKEN_JOY_BOMB 23
1672 #define SETUP_TOKEN_KEY_LEFT 24
1673 #define SETUP_TOKEN_KEY_RIGHT 25
1674 #define SETUP_TOKEN_KEY_UP 26
1675 #define SETUP_TOKEN_KEY_DOWN 27
1676 #define SETUP_TOKEN_KEY_SNAP 28
1677 #define SETUP_TOKEN_KEY_BOMB 29
1679 /* level directory info */
1680 #define LEVELINFO_TOKEN_NAME 30
1681 #define LEVELINFO_TOKEN_NAME_SHORT 31
1682 #define LEVELINFO_TOKEN_NAME_SORTING 32
1683 #define LEVELINFO_TOKEN_AUTHOR 33
1684 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1685 #define LEVELINFO_TOKEN_LEVELS 35
1686 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1687 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1688 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1689 #define LEVELINFO_TOKEN_READONLY 39
1691 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1692 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1694 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1695 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1697 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1698 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1700 #define TYPE_BOOLEAN 1
1701 #define TYPE_SWITCH 2
1703 #define TYPE_INTEGER 4
1704 #define TYPE_STRING 5
1706 static struct SetupInfo si;
1707 static struct SetupInputInfo sii;
1708 static struct LevelDirInfo ldi;
1717 { TYPE_STRING, &si.player_name, "player_name" },
1718 { TYPE_SWITCH, &si.sound, "sound" },
1719 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1720 { TYPE_SWITCH, &si.sound_music, "background_music" },
1721 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1724 { TYPE_SWITCH, &si.toons, "toons" },
1725 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1728 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1729 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1730 { TYPE_SWITCH, &si.fading, "screen_fading" },
1731 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1732 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1733 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1734 { TYPE_SWITCH, &si.handicap, "handicap" },
1735 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1736 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1739 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1740 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1741 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1742 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1743 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1744 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1745 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1746 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1747 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1748 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1749 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1750 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1751 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1752 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1753 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1754 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1756 /* level directory info */
1757 { TYPE_STRING, &ldi.name, "name" },
1758 { TYPE_STRING, &ldi.name_short, "name_short" },
1759 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1760 { TYPE_STRING, &ldi.author, "author" },
1761 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1762 { TYPE_INTEGER, &ldi.levels, "levels" },
1763 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1764 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1765 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1766 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1769 static char *string_tolower(char *s)
1771 static char s_lower[100];
1774 if (strlen(s) >= 100)
1779 for (i=0; i<strlen(s_lower); i++)
1780 s_lower[i] = tolower(s_lower[i]);
1785 static int get_string_integer_value(char *s)
1787 static char *number_text[][3] =
1789 { "0", "zero", "null", },
1790 { "1", "one", "first" },
1791 { "2", "two", "second" },
1792 { "3", "three", "third" },
1793 { "4", "four", "fourth" },
1794 { "5", "five", "fifth" },
1795 { "6", "six", "sixth" },
1796 { "7", "seven", "seventh" },
1797 { "8", "eight", "eighth" },
1798 { "9", "nine", "ninth" },
1799 { "10", "ten", "tenth" },
1800 { "11", "eleven", "eleventh" },
1801 { "12", "twelve", "twelfth" },
1806 for (i=0; i<13; i++)
1808 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1814 static boolean get_string_boolean_value(char *s)
1816 if (strcmp(string_tolower(s), "true") == 0 ||
1817 strcmp(string_tolower(s), "yes") == 0 ||
1818 strcmp(string_tolower(s), "on") == 0 ||
1819 get_string_integer_value(s) == 1)
1825 static char *getFormattedSetupEntry(char *token, char *value)
1828 static char entry[MAX_LINE_LEN];
1830 sprintf(entry, "%s:", token);
1831 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1835 strcat(entry, value);
1840 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1842 if (!setup_file_list)
1845 if (setup_file_list->token)
1846 free(setup_file_list->token);
1847 if (setup_file_list->value)
1848 free(setup_file_list->value);
1849 if (setup_file_list->next)
1850 freeSetupFileList(setup_file_list->next);
1851 free(setup_file_list);
1854 static struct SetupFileList *newSetupFileList(char *token, char *value)
1856 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1858 new->token = checked_malloc(strlen(token) + 1);
1859 strcpy(new->token, token);
1861 new->value = checked_malloc(strlen(value) + 1);
1862 strcpy(new->value, value);
1869 static char *getTokenValue(struct SetupFileList *setup_file_list,
1872 if (!setup_file_list)
1875 if (strcmp(setup_file_list->token, token) == 0)
1876 return setup_file_list->value;
1878 return getTokenValue(setup_file_list->next, token);
1881 static void setTokenValue(struct SetupFileList *setup_file_list,
1882 char *token, char *value)
1884 if (!setup_file_list)
1887 if (strcmp(setup_file_list->token, token) == 0)
1889 free(setup_file_list->value);
1890 setup_file_list->value = checked_malloc(strlen(value) + 1);
1891 strcpy(setup_file_list->value, value);
1893 else if (setup_file_list->next == NULL)
1894 setup_file_list->next = newSetupFileList(token, value);
1896 setTokenValue(setup_file_list->next, token, value);
1900 static void printSetupFileList(struct SetupFileList *setup_file_list)
1902 if (!setup_file_list)
1905 printf("token: '%s'\n", setup_file_list->token);
1906 printf("value: '%s'\n", setup_file_list->value);
1908 printSetupFileList(setup_file_list->next);
1912 static struct SetupFileList *loadSetupFileList(char *filename)
1915 char line[MAX_LINE_LEN];
1916 char *token, *value, *line_ptr;
1917 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1918 struct SetupFileList *first_valid_list_entry;
1922 if (!(file = fopen(filename, MODE_READ)))
1924 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1930 /* read next line of input file */
1931 if (!fgets(line, MAX_LINE_LEN, file))
1934 /* cut trailing comment or whitespace from input line */
1935 for (line_ptr = line; *line_ptr; line_ptr++)
1937 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1944 /* cut trailing whitespaces from input line */
1945 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1946 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1949 /* ignore empty lines */
1953 line_len = strlen(line);
1955 /* cut leading whitespaces from token */
1956 for (token = line; *token; token++)
1957 if (*token != ' ' && *token != '\t')
1960 /* find end of token */
1961 for (line_ptr = token; *line_ptr; line_ptr++)
1963 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1970 if (line_ptr < line + line_len)
1971 value = line_ptr + 1;
1975 /* cut leading whitespaces from value */
1976 for (; *value; value++)
1977 if (*value != ' ' && *value != '\t')
1980 if (*token && *value)
1981 setTokenValue(setup_file_list, token, value);
1986 first_valid_list_entry = setup_file_list->next;
1988 /* free empty list header */
1989 setup_file_list->next = NULL;
1990 freeSetupFileList(setup_file_list);
1992 if (first_valid_list_entry == NULL)
1993 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1995 return first_valid_list_entry;
1998 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
2001 if (!setup_file_list)
2004 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2006 if (strcmp(setup_file_list->value, identifier) != 0)
2008 Error(ERR_WARN, "configuration file has wrong version");
2015 if (setup_file_list->next)
2016 checkSetupFileListIdentifier(setup_file_list->next, identifier);
2019 Error(ERR_WARN, "configuration file has no version information");
2024 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2026 ldi->filename = NULL;
2027 ldi->fullpath = NULL;
2028 ldi->basepath = NULL;
2029 ldi->name = getStringCopy(ANONYMOUS_NAME);
2030 ldi->name_short = NULL;
2031 ldi->name_sorting = NULL;
2032 ldi->author = getStringCopy(ANONYMOUS_NAME);
2033 ldi->imported_from = NULL;
2035 ldi->first_level = 0;
2036 ldi->last_level = 0;
2037 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
2038 ldi->level_group = FALSE;
2039 ldi->parent_link = FALSE;
2040 ldi->user_defined = FALSE;
2041 ldi->readonly = TRUE;
2043 ldi->class_desc = NULL;
2044 ldi->handicap_level = 0;
2046 ldi->cl_cursor = -1;
2048 ldi->node_parent = NULL;
2049 ldi->node_group = NULL;
2053 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2054 struct LevelDirInfo *parent)
2058 setLevelDirInfoToDefaults(ldi);
2062 /* first copy all values from the parent structure ... */
2065 /* ... then set all fields to default that cannot be inherited from parent.
2066 This is especially important for all those fields that can be set from
2067 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2068 calls 'free()' for all already set token values which requires that no
2069 other structure's pointer may point to them!
2072 ldi->filename = NULL;
2073 ldi->fullpath = NULL;
2074 ldi->basepath = NULL;
2075 ldi->name = getStringCopy(ANONYMOUS_NAME);
2076 ldi->name_short = NULL;
2077 ldi->name_sorting = NULL;
2078 ldi->author = getStringCopy(parent->author);
2079 ldi->imported_from = getStringCopy(parent->imported_from);
2081 ldi->level_group = FALSE;
2082 ldi->parent_link = FALSE;
2084 ldi->node_parent = parent;
2085 ldi->node_group = NULL;
2089 static void setSetupInfoToDefaults(struct SetupInfo *si)
2093 si->player_name = getStringCopy(getLoginName());
2096 si->sound_loops = TRUE;
2097 si->sound_music = TRUE;
2098 si->sound_simple = TRUE;
2100 si->double_buffering = TRUE;
2101 si->direct_draw = !si->double_buffering;
2102 si->scroll_delay = TRUE;
2103 si->soft_scrolling = TRUE;
2105 si->autorecord = TRUE;
2106 si->quick_doors = FALSE;
2107 si->team_mode = FALSE;
2108 si->handicap = TRUE;
2109 si->time_limit = TRUE;
2110 si->fullscreen = FALSE;
2112 for (i=0; i<MAX_PLAYERS; i++)
2114 si->input[i].use_joystick = FALSE;
2115 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2116 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2117 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2118 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2119 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2120 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2121 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2122 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2123 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2124 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2125 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2126 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2127 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2128 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2129 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2133 static void setSetupInfo(int token_nr, char *token_value)
2135 int token_type = token_info[token_nr].type;
2136 void *setup_value = token_info[token_nr].value;
2138 if (token_value == NULL)
2141 /* set setup field to corresponding token value */
2146 *(boolean *)setup_value = get_string_boolean_value(token_value);
2150 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2154 *(int *)setup_value = get_string_integer_value(token_value);
2158 if (*(char **)setup_value != NULL)
2159 free(*(char **)setup_value);
2160 *(char **)setup_value = getStringCopy(token_value);
2168 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2172 if (!setup_file_list)
2175 /* handle global setup values */
2177 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2178 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2181 /* handle player specific setup values */
2182 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2186 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2188 sii = setup.input[pnr];
2189 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2191 char full_token[100];
2193 sprintf(full_token, "%s%s", prefix, token_info[i].text);
2194 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2196 setup.input[pnr] = sii;
2200 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2202 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2203 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2206 if (entry1->parent_link || entry2->parent_link)
2207 compare_result = (entry1->parent_link ? -1 : +1);
2208 else if (entry1->sort_priority == entry2->sort_priority)
2210 char *name1 = getStringToLower(entry1->name_sorting);
2211 char *name2 = getStringToLower(entry2->name_sorting);
2213 compare_result = strcmp(name1, name2);
2218 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2219 compare_result = entry1->sort_priority - entry2->sort_priority;
2221 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2223 return compare_result;
2226 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2228 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2230 setLevelDirInfoToDefaults(leveldir_new);
2232 leveldir_new->node_parent = node_parent;
2233 leveldir_new->parent_link = TRUE;
2235 leveldir_new->name = ".. (parent directory)";
2236 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2237 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2239 leveldir_new->filename = "..";
2240 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2242 leveldir_new->sort_priority = node_parent->sort_priority;
2243 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2245 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2248 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2249 struct LevelDirInfo *node_parent,
2250 char *level_directory)
2253 struct dirent *dir_entry;
2254 boolean valid_entry_found = FALSE;
2256 if ((dir = opendir(level_directory)) == NULL)
2258 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2262 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2264 struct SetupFileList *setup_file_list = NULL;
2265 struct stat file_status;
2266 char *directory_name = dir_entry->d_name;
2267 char *directory_path = getPath2(level_directory, directory_name);
2268 char *filename = NULL;
2270 /* skip entries for current and parent directory */
2271 if (strcmp(directory_name, ".") == 0 ||
2272 strcmp(directory_name, "..") == 0)
2274 free(directory_path);
2278 /* find out if directory entry is itself a directory */
2279 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2280 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2282 free(directory_path);
2286 filename = getPath2(directory_path, LEVELINFO_FILENAME);
2287 setup_file_list = loadSetupFileList(filename);
2289 if (setup_file_list)
2291 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2294 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2295 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2297 /* set all structure fields according to the token/value pairs */
2298 ldi = *leveldir_new;
2299 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2300 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2301 *leveldir_new = ldi;
2303 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2305 if (leveldir_new->name_short == NULL)
2306 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2308 if (leveldir_new->name_sorting == NULL)
2309 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2311 leveldir_new->filename = getStringCopy(directory_name);
2313 if (node_parent == NULL) /* top level group */
2315 leveldir_new->basepath = level_directory;
2316 leveldir_new->fullpath = leveldir_new->filename;
2318 else /* sub level group */
2320 leveldir_new->basepath = node_parent->basepath;
2321 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2325 if (leveldir_new->levels < 1)
2326 leveldir_new->levels = 1;
2328 leveldir_new->last_level =
2329 leveldir_new->first_level + leveldir_new->levels - 1;
2331 leveldir_new->user_defined =
2332 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2334 leveldir_new->color = LEVELCOLOR(leveldir_new);
2335 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2337 leveldir_new->handicap_level = /* set handicap to default value */
2338 (leveldir_new->user_defined ?
2339 leveldir_new->last_level :
2340 leveldir_new->first_level);
2342 pushLevelDirInfo(node_first, leveldir_new);
2344 freeSetupFileList(setup_file_list);
2345 valid_entry_found = TRUE;
2347 if (leveldir_new->level_group)
2349 /* create node to link back to current level directory */
2350 createParentLevelDirNode(leveldir_new);
2352 /* step into sub-directory and look for more level series */
2353 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2354 leveldir_new, directory_path);
2358 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2360 free(directory_path);
2366 if (!valid_entry_found)
2367 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2371 void LoadLevelInfo()
2373 InitUserLevelDirectory(getLoginName());
2375 DrawInitText("Loading level series:", 120, FC_GREEN);
2377 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2378 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2380 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2382 if (leveldir_first == NULL)
2383 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2385 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2388 dumpLevelDirInfo(leveldir_first, 0);
2392 static void SaveUserLevelInfo()
2398 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2400 if (!(file = fopen(filename, MODE_WRITE)))
2402 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2407 /* always start with reliable default values */
2408 setLevelDirInfoToDefaults(&ldi);
2410 ldi.name = getLoginName();
2411 ldi.author = getRealName();
2413 ldi.first_level = 1;
2414 ldi.sort_priority = LEVELCLASS_USER_START;
2415 ldi.readonly = FALSE;
2417 fprintf(file, "%s\n\n",
2418 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2420 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2421 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2422 i != LEVELINFO_TOKEN_NAME_SORTING &&
2423 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2424 fprintf(file, "%s\n", getSetupLine("", i));
2429 chmod(filename, SETUP_PERMS);
2435 struct SetupFileList *setup_file_list = NULL;
2437 /* always start with reliable default values */
2438 setSetupInfoToDefaults(&setup);
2440 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2442 setup_file_list = loadSetupFileList(filename);
2444 if (setup_file_list)
2446 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2447 decodeSetupFileList(setup_file_list);
2449 setup.direct_draw = !setup.double_buffering;
2451 freeSetupFileList(setup_file_list);
2453 /* needed to work around problems with fixed length strings */
2454 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2455 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2456 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2458 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2460 strcpy(new_name, setup.player_name);
2461 free(setup.player_name);
2462 setup.player_name = new_name;
2466 Error(ERR_WARN, "using default setup values");
2471 static char *getSetupLine(char *prefix, int token_nr)
2474 static char entry[MAX_LINE_LEN];
2475 int token_type = token_info[token_nr].type;
2476 void *setup_value = token_info[token_nr].value;
2477 char *token_text = token_info[token_nr].text;
2479 /* start with the prefix, token and some spaces to format output line */
2480 sprintf(entry, "%s%s:", prefix, token_text);
2481 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2484 /* continue with the token's value (which can have different types) */
2488 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2492 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2497 Key key = *(Key *)setup_value;
2498 char *keyname = getKeyNameFromKey(key);
2500 strcat(entry, getX11KeyNameFromKey(key));
2501 for (i=strlen(entry); i<50; i++)
2504 /* add comment, if useful */
2505 if (strcmp(keyname, "(undefined)") != 0 &&
2506 strcmp(keyname, "(unknown)") != 0)
2508 strcat(entry, "# ");
2509 strcat(entry, keyname);
2516 char buffer[MAX_LINE_LEN];
2518 sprintf(buffer, "%d", *(int *)setup_value);
2519 strcat(entry, buffer);
2524 strcat(entry, *(char **)setup_value);
2540 InitUserDataDirectory();
2542 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2544 if (!(file = fopen(filename, MODE_WRITE)))
2546 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2551 fprintf(file, "%s\n",
2552 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2553 fprintf(file, "\n");
2555 /* handle global setup values */
2557 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2559 fprintf(file, "%s\n", getSetupLine("", i));
2561 /* just to make things nicer :) */
2562 if (i == SETUP_TOKEN_PLAYER_NAME)
2563 fprintf(file, "\n");
2566 /* handle player specific setup values */
2567 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2571 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2572 fprintf(file, "\n");
2574 sii = setup.input[pnr];
2575 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2576 fprintf(file, "%s\n", getSetupLine(prefix, i));
2582 chmod(filename, SETUP_PERMS);
2585 void LoadLevelSetup_LastSeries()
2588 struct SetupFileList *level_setup_list = NULL;
2590 /* always start with reliable default values */
2591 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2593 /* ----------------------------------------------------------------------- */
2594 /* ~/.rocksndiamonds/levelsetup.conf */
2595 /* ----------------------------------------------------------------------- */
2597 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2599 if ((level_setup_list = loadSetupFileList(filename)))
2601 char *last_level_series =
2602 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2604 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2605 if (leveldir_current == NULL)
2606 leveldir_current = leveldir_first;
2608 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2610 freeSetupFileList(level_setup_list);
2613 Error(ERR_WARN, "using default setup values");
2618 void SaveLevelSetup_LastSeries()
2621 char *level_subdir = leveldir_current->filename;
2624 /* ----------------------------------------------------------------------- */
2625 /* ~/.rocksndiamonds/levelsetup.conf */
2626 /* ----------------------------------------------------------------------- */
2628 InitUserDataDirectory();
2630 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2632 if (!(file = fopen(filename, MODE_WRITE)))
2634 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2639 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2640 LEVELSETUP_COOKIE));
2641 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2647 chmod(filename, SETUP_PERMS);
2650 static void checkSeriesInfo()
2652 static char *level_directory = NULL;
2654 struct dirent *dir_entry;
2656 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2658 level_directory = getPath2((leveldir_current->user_defined ?
2659 getUserLevelDir("") :
2660 options.level_directory),
2661 leveldir_current->fullpath);
2663 if ((dir = opendir(level_directory)) == NULL)
2665 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2669 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2671 if (strlen(dir_entry->d_name) > 4 &&
2672 dir_entry->d_name[3] == '.' &&
2673 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2675 char levelnum_str[4];
2678 strncpy(levelnum_str, dir_entry->d_name, 3);
2679 levelnum_str[3] = '\0';
2681 levelnum_value = atoi(levelnum_str);
2683 if (levelnum_value < leveldir_current->first_level)
2685 Error(ERR_WARN, "additional level %d found", levelnum_value);
2686 leveldir_current->first_level = levelnum_value;
2688 else if (levelnum_value > leveldir_current->last_level)
2690 Error(ERR_WARN, "additional level %d found", levelnum_value);
2691 leveldir_current->last_level = levelnum_value;
2699 void LoadLevelSetup_SeriesInfo()
2702 struct SetupFileList *level_setup_list = NULL;
2703 char *level_subdir = leveldir_current->filename;
2705 /* always start with reliable default values */
2706 level_nr = leveldir_current->first_level;
2708 checkSeriesInfo(leveldir_current);
2710 /* ----------------------------------------------------------------------- */
2711 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2712 /* ----------------------------------------------------------------------- */
2714 level_subdir = leveldir_current->filename;
2716 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2718 if ((level_setup_list = loadSetupFileList(filename)))
2722 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2726 level_nr = atoi(token_value);
2728 if (level_nr < leveldir_current->first_level)
2729 level_nr = leveldir_current->first_level;
2730 if (level_nr > leveldir_current->last_level)
2731 level_nr = leveldir_current->last_level;
2734 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2738 int level_nr = atoi(token_value);
2740 if (level_nr < leveldir_current->first_level)
2741 level_nr = leveldir_current->first_level;
2742 if (level_nr > leveldir_current->last_level + 1)
2743 level_nr = leveldir_current->last_level;
2745 if (leveldir_current->user_defined)
2746 level_nr = leveldir_current->last_level;
2748 leveldir_current->handicap_level = level_nr;
2751 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2753 freeSetupFileList(level_setup_list);
2756 Error(ERR_WARN, "using default setup values");
2761 void SaveLevelSetup_SeriesInfo()
2764 char *level_subdir = leveldir_current->filename;
2765 char *level_nr_str = int2str(level_nr, 0);
2766 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2769 /* ----------------------------------------------------------------------- */
2770 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2771 /* ----------------------------------------------------------------------- */
2773 InitLevelSetupDirectory(level_subdir);
2775 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2777 if (!(file = fopen(filename, MODE_WRITE)))
2779 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2784 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2785 LEVELSETUP_COOKIE));
2786 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2788 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2789 handicap_level_str));
2794 chmod(filename, SETUP_PERMS);
2796 /* LocalWords: Rocks'n