1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-98 Artsoft Entertainment *
8 * phone: ++49 +521 290471 *
9 * email: aeglos@valinor.owl.de *
10 *----------------------------------------------------------*
12 ***********************************************************/
24 #define MAX_FILENAME_LEN 256 /* maximal filename length */
25 #define MAX_LINE_LEN 1000 /* maximal input line length */
26 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
27 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
28 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
29 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
30 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
31 #define FILE_VERSION_1_0 10 /* 1.0 file version (old) */
32 #define FILE_VERSION_1_2 12 /* 1.2 file version (still in use) */
33 #define FILE_VERSION_1_4 14 /* 1.4 file version (new) */
35 /* file identifier strings */
36 #define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
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 TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
47 /* file names and filename extensions */
49 #define USERDATA_DIRECTORY ".rocksndiamonds"
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 USERDATA_DIRECTORY "userdata"
59 #define LEVELSETUP_DIRECTORY "lvlsetup"
60 #define SETUP_FILENAME "setup.cnf"
61 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
62 #define LEVELINFO_FILENAME "lvlinfo.cnf"
63 #define LEVELFILE_EXTENSION "lvl"
64 #define TAPEFILE_EXTENSION "tap"
65 #define SCOREFILE_EXTENSION "sco"
68 #if defined(MSDOS) || defined(WIN32)
69 #define ERROR_FILENAME "error.out"
74 #define S_IRGRP S_IRUSR
77 #define S_IROTH S_IRUSR
80 #define S_IWGRP S_IWUSR
83 #define S_IWOTH S_IWUSR
86 #define S_IXGRP S_IXUSR
89 #define S_IXOTH S_IXUSR
93 /* file permissions for newly written files */
94 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
95 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
96 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
97 #define USERDATA_DIR_MODE (MODE_R_ALL | MODE_X_ALL | S_IWUSR)
98 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
99 #define SCORE_PERMS LEVEL_PERMS
100 #define TAPE_PERMS LEVEL_PERMS
101 #define SETUP_PERMS LEVEL_PERMS
103 /* sort priorities of level series (also used as level series classes) */
104 #define LEVELCLASS_TUTORIAL_START 10
105 #define LEVELCLASS_TUTORIAL_END 99
106 #define LEVELCLASS_CLASSICS_START 100
107 #define LEVELCLASS_CLASSICS_END 199
108 #define LEVELCLASS_CONTRIBUTION_START 200
109 #define LEVELCLASS_CONTRIBUTION_END 299
110 #define LEVELCLASS_USER_START 300
111 #define LEVELCLASS_USER_END 399
112 #define LEVELCLASS_BD_START 400
113 #define LEVELCLASS_BD_END 499
114 #define LEVELCLASS_EM_START 500
115 #define LEVELCLASS_EM_END 599
116 #define LEVELCLASS_SP_START 600
117 #define LEVELCLASS_SP_END 699
118 #define LEVELCLASS_DX_START 700
119 #define LEVELCLASS_DX_END 799
121 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
122 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
123 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
124 #define LEVELCLASS_USER LEVELCLASS_USER_START
125 #define LEVELCLASS_BD LEVELCLASS_BD_START
126 #define LEVELCLASS_EM LEVELCLASS_EM_START
127 #define LEVELCLASS_SP LEVELCLASS_SP_START
128 #define LEVELCLASS_DX LEVELCLASS_DX_START
130 #define LEVELCLASS_UNDEFINED 999
132 #define NUM_LEVELCLASS_DESC 8
133 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
145 #define IS_LEVELCLASS_TUTORIAL(p) \
146 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
147 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
148 #define IS_LEVELCLASS_CLASSICS(p) \
149 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
150 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
151 #define IS_LEVELCLASS_CONTRIBUTION(p) \
152 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
153 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
154 #define IS_LEVELCLASS_USER(p) \
155 ((p)->sort_priority >= LEVELCLASS_USER_START && \
156 (p)->sort_priority <= LEVELCLASS_USER_END)
157 #define IS_LEVELCLASS_BD(p) \
158 ((p)->sort_priority >= LEVELCLASS_BD_START && \
159 (p)->sort_priority <= LEVELCLASS_BD_END)
160 #define IS_LEVELCLASS_EM(p) \
161 ((p)->sort_priority >= LEVELCLASS_EM_START && \
162 (p)->sort_priority <= LEVELCLASS_EM_END)
163 #define IS_LEVELCLASS_SP(p) \
164 ((p)->sort_priority >= LEVELCLASS_SP_START && \
165 (p)->sort_priority <= LEVELCLASS_SP_END)
166 #define IS_LEVELCLASS_DX(p) \
167 ((p)->sort_priority >= LEVELCLASS_DX_START && \
168 (p)->sort_priority <= LEVELCLASS_DX_END)
170 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
171 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
172 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
173 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
174 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
175 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
176 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
177 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
178 LEVELCLASS_UNDEFINED)
180 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
181 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
182 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
183 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
184 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
185 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
186 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
187 IS_LEVELCLASS_USER(n) ? FC_RED : \
190 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
191 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
192 IS_LEVELCLASS_BD(n) ? 2 : \
193 IS_LEVELCLASS_EM(n) ? 3 : \
194 IS_LEVELCLASS_SP(n) ? 4 : \
195 IS_LEVELCLASS_DX(n) ? 5 : \
196 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
197 IS_LEVELCLASS_USER(n) ? 7 : \
200 char *getLevelClassDescription(struct LevelDirInfo *ldi)
202 int position = ldi->sort_priority / 100;
204 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
205 return levelclass_desc[position];
207 return "Unknown Level Class";
210 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
211 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
213 char *getUserDataDir()
215 static char *userdata_dir = NULL;
219 char *home_dir = getHomeDir();
220 char *data_dir = USERDATA_DIRECTORY;
222 userdata_dir = getPath2(home_dir, data_dir);
228 static char *getSetupDir()
230 return getUserDataDir();
233 static char *getUserLevelDir(char *level_subdir)
235 static char *userlevel_dir = NULL;
236 char *data_dir = getUserDataDir();
237 char *userlevel_subdir = LEVELS_DIRECTORY;
242 if (strlen(level_subdir) > 0)
243 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
245 userlevel_dir = getPath2(data_dir, userlevel_subdir);
247 return userlevel_dir;
250 static char *getTapeDir(char *level_subdir)
252 static char *tape_dir = NULL;
253 char *data_dir = getUserDataDir();
254 char *tape_subdir = TAPES_DIRECTORY;
259 if (strlen(level_subdir) > 0)
260 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
262 tape_dir = getPath2(data_dir, tape_subdir);
267 static char *getScoreDir(char *level_subdir)
269 static char *score_dir = NULL;
270 char *data_dir = options.rw_base_directory;
271 char *score_subdir = SCORES_DIRECTORY;
276 if (strlen(level_subdir) > 0)
277 score_dir = getPath3(data_dir, score_subdir, level_subdir);
279 score_dir = getPath2(data_dir, score_subdir);
284 static char *getLevelSetupDir(char *level_subdir)
286 static char *levelsetup_dir = NULL;
287 char *data_dir = getUserDataDir();
288 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
291 free(levelsetup_dir);
293 if (strlen(level_subdir) > 0)
294 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
296 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
298 return levelsetup_dir;
301 static char *getLevelFilename(int nr)
303 static char *filename = NULL;
304 char basename[MAX_FILENAME_LEN];
306 if (filename != NULL)
309 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
310 filename = getPath3((leveldir_current->user_defined ?
311 getUserLevelDir("") :
312 options.level_directory),
313 leveldir_current->fullpath,
319 static char *getTapeFilename(int nr)
321 static char *filename = NULL;
322 char basename[MAX_FILENAME_LEN];
324 if (filename != NULL)
327 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
328 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
333 static char *getScoreFilename(int nr)
335 static char *filename = NULL;
336 char basename[MAX_FILENAME_LEN];
338 if (filename != NULL)
341 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
342 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
347 static void createDirectory(char *dir, char *text)
349 if (access(dir, F_OK) != 0)
353 if (mkdir(dir, USERDATA_DIR_MODE) != 0)
355 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
358 static void InitUserDataDirectory()
360 createDirectory(getUserDataDir(), "user data");
363 static void InitTapeDirectory(char *level_subdir)
365 createDirectory(getUserDataDir(), "user data");
366 createDirectory(getTapeDir(""), "main tape");
367 createDirectory(getTapeDir(level_subdir), "level tape");
370 static void InitScoreDirectory(char *level_subdir)
372 createDirectory(getScoreDir(""), "main score");
373 createDirectory(getScoreDir(level_subdir), "level score");
376 static void InitUserLevelDirectory(char *level_subdir)
378 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
380 createDirectory(getUserDataDir(), "user data");
381 createDirectory(getUserLevelDir(""), "main user level");
382 createDirectory(getUserLevelDir(level_subdir), "user level");
388 static void InitLevelSetupDirectory(char *level_subdir)
390 createDirectory(getUserDataDir(), "user data");
391 createDirectory(getLevelSetupDir(""), "main level setup");
392 createDirectory(getLevelSetupDir(level_subdir), "level setup");
395 static void setLevelInfoToDefaults()
399 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
400 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
402 for(x=0; x<MAX_LEV_FIELDX; x++)
403 for(y=0; y<MAX_LEV_FIELDY; y++)
404 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
407 level.gems_needed = 0;
408 level.amoeba_speed = 10;
409 level.time_magic_wall = 10;
410 level.time_wheel = 10;
411 level.time_light = 10;
412 level.time_timegate = 10;
413 level.amoeba_content = EL_DIAMANT;
414 level.double_speed = FALSE;
415 level.gravity = FALSE;
417 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
418 level.name[i] = '\0';
419 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
420 level.author[i] = '\0';
422 strcpy(level.name, NAMELESS_LEVEL_NAME);
423 strcpy(level.author, ANONYMOUS_NAME);
425 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
428 level.num_yam_contents = STD_ELEMENT_CONTENTS;
429 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
432 level.yam_content[i][x][y] = EL_FELSBROCKEN;
434 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
435 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
436 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
438 BorderElement = EL_BETON;
440 /* try to determine better author name than 'anonymous' */
441 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
443 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
444 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
448 switch (LEVELCLASS(leveldir_current))
450 case LEVELCLASS_TUTORIAL:
451 strcpy(level.author, PROGRAM_AUTHOR_STRING);
454 case LEVELCLASS_CONTRIBUTION:
455 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
456 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
459 case LEVELCLASS_USER:
460 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
461 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
465 /* keep default value */
471 static int checkLevelElement(int element)
473 if (element >= EL_FIRST_RUNTIME_EL)
475 Error(ERR_WARN, "invalid level element %d", element);
476 element = EL_CHAR_FRAGE;
482 void LoadLevel(int level_nr)
485 char *filename = getLevelFilename(level_nr);
486 char cookie[MAX_LINE_LEN];
487 char chunk[CHUNK_ID_LEN + 1];
488 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
489 int file_version = FILE_VERSION_1_4; /* last version of level files */
493 /* always start with reliable default values */
494 setLevelInfoToDefaults();
496 if (!(file = fopen(filename, "r")))
498 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
502 /* check file identifier */
503 fgets(cookie, MAX_LINE_LEN, file);
504 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
505 cookie[strlen(cookie) - 1] = '\0';
507 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
508 file_version = FILE_VERSION_1_0;
509 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
510 file_version = FILE_VERSION_1_2;
511 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
513 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
518 /* read chunk "HEAD" */
519 if (file_version >= FILE_VERSION_1_2)
521 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
522 if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
524 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
530 lev_fieldx = level.fieldx = fgetc(file);
531 lev_fieldy = level.fieldy = fgetc(file);
533 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
534 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
536 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
537 level.name[i] = fgetc(file);
538 level.name[MAX_LEVEL_NAME_LEN] = 0;
540 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
541 level.score[i] = fgetc(file);
543 level.num_yam_contents = STD_ELEMENT_CONTENTS;
544 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
550 if (i < STD_ELEMENT_CONTENTS)
551 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
553 level.yam_content[i][x][y] = EL_LEERRAUM;
558 level.amoeba_speed = fgetc(file);
559 level.time_magic_wall = fgetc(file);
560 level.time_wheel = fgetc(file);
561 level.amoeba_content = checkLevelElement(fgetc(file));
562 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
563 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
565 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
567 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
570 if (file_version >= FILE_VERSION_1_2)
572 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
574 /* look for optional author chunk */
575 if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
577 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
578 level.author[i] = fgetc(file);
579 level.author[MAX_LEVEL_NAME_LEN] = 0;
581 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
584 /* look for optional content chunk */
585 if (strcmp(chunk, "CONT") == 0 &&
586 chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
589 level.num_yam_contents = fgetc(file);
593 if (level.num_yam_contents < 1 ||
594 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
597 printf("WARNING: num_yam_contents == %d (corrected)\n",
598 level.num_yam_contents);
600 level.num_yam_contents = STD_ELEMENT_CONTENTS;
603 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
606 level.yam_content[i][x][y] =
607 checkLevelElement(encoding_16bit ?
608 getFile16BitInteger(file,
609 BYTE_ORDER_BIG_ENDIAN) :
612 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
615 /* next check body chunk identifier and chunk length */
616 if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
618 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
624 /* clear all other level fields (needed if resized in level editor later) */
625 for(x=0; x<MAX_LEV_FIELDX; x++)
626 for(y=0; y<MAX_LEV_FIELDY; y++)
627 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
629 /* now read in the valid level fields from level file */
630 for(y=0; y<lev_fieldy; y++)
631 for(x=0; x<lev_fieldx; x++)
632 Feld[x][y] = Ur[x][y] =
633 checkLevelElement(encoding_16bit ?
634 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
639 /* player was faster than monsters in pre-1.0 levels */
640 if (file_version == FILE_VERSION_1_0 &&
641 IS_LEVELCLASS_CONTRIBUTION(leveldir_current))
643 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
644 Error(ERR_WARN, "using high speed movement for player");
645 level.double_speed = TRUE;
648 /* determine border element for this level */
652 void SaveLevel(int level_nr)
655 char *filename = getLevelFilename(level_nr);
656 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
657 char *oldest_possible_cookie;
660 if (!(file = fopen(filename, "w")))
662 Error(ERR_WARN, "cannot save level file '%s'", filename);
666 /* check yam content for 16-bit elements */
667 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
670 if (level.yam_content[i][x][y] > 255)
671 encoding_16bit = TRUE;
673 /* check level field for 16-bit elements */
674 for(y=0; y<lev_fieldy; y++)
675 for(x=0; x<lev_fieldx; x++)
677 encoding_16bit = TRUE;
679 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
681 fputs(oldest_possible_cookie, file); /* file identifier */
684 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
686 fputc(level.fieldx, file);
687 fputc(level.fieldy, file);
689 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
690 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
692 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
693 fputc(level.name[i], file);
694 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
695 fputc(level.score[i], file);
696 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
699 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
700 fputc(level.amoeba_speed, file);
701 fputc(level.time_magic_wall, file);
702 fputc(level.time_wheel, file);
703 fputc(level.amoeba_content, file);
704 fputc((level.double_speed ? 1 : 0), file);
705 fputc((level.gravity ? 1 : 0), file);
707 fputc((encoding_16bit ? 1 : 0), file);
709 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
712 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
714 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
715 fputc(level.author[i], file);
717 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
718 BYTE_ORDER_BIG_ENDIAN);
720 fputc(EL_MAMPFER, file);
721 fputc(level.num_yam_contents, file);
725 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
729 putFile16BitInteger(file, level.yam_content[i][x][y],
730 BYTE_ORDER_BIG_ENDIAN);
732 fputc(level.yam_content[i][x][y], file);
734 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
736 for(y=0; y<lev_fieldy; y++)
737 for(x=0; x<lev_fieldx; x++)
739 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
741 fputc(Ur[x][y], file);
745 chmod(filename, LEVEL_PERMS);
748 void LoadTape(int level_nr)
751 char *filename = getTapeFilename(level_nr);
752 char cookie[MAX_LINE_LEN];
753 char chunk[CHUNK_ID_LEN + 1];
755 int num_participating_players;
756 int file_version = FILE_VERSION_1_2; /* last version of tape files */
759 /* always start with reliable default values (empty tape) */
762 /* default values (also for pre-1.2 tapes) with only the first player */
763 tape.player_participates[0] = TRUE;
764 for(i=1; i<MAX_PLAYERS; i++)
765 tape.player_participates[i] = FALSE;
767 /* at least one (default: the first) player participates in every tape */
768 num_participating_players = 1;
770 if (!(file = fopen(filename, "r")))
773 /* check file identifier */
774 fgets(cookie, MAX_LINE_LEN, file);
775 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
776 cookie[strlen(cookie) - 1] = '\0';
778 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
779 file_version = FILE_VERSION_1_0;
780 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
782 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
787 /* read chunk "HEAD" */
788 if (file_version >= FILE_VERSION_1_2)
790 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
791 if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
793 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
799 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
800 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
801 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
803 /* read header fields that are new since version 1.2 */
804 if (file_version >= FILE_VERSION_1_2)
806 byte store_participating_players = fgetc(file);
808 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
811 /* since version 1.2, tapes store which players participate in the tape */
812 num_participating_players = 0;
813 for(i=0; i<MAX_PLAYERS; i++)
815 tape.player_participates[i] = FALSE;
817 if (store_participating_players & (1 << i))
819 tape.player_participates[i] = TRUE;
820 num_participating_players++;
825 tape.level_nr = level_nr;
827 tape.changed = FALSE;
829 tape.recording = FALSE;
830 tape.playing = FALSE;
831 tape.pausing = FALSE;
833 /* read chunk "BODY" */
834 if (file_version >= FILE_VERSION_1_2)
836 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
837 if (strcmp(chunk, "BODY") ||
838 chunk_length != (num_participating_players + 1) * tape.length)
840 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
846 for(i=0; i<tape.length; i++)
848 if (i >= MAX_TAPELEN)
851 for(j=0; j<MAX_PLAYERS; j++)
853 tape.pos[i].action[j] = MV_NO_MOVING;
855 if (tape.player_participates[j])
856 tape.pos[i].action[j] = fgetc(file);
859 tape.pos[i].delay = fgetc(file);
861 if (file_version == FILE_VERSION_1_0)
863 /* eliminate possible diagonal moves in old tapes */
864 /* this is only for backward compatibility */
866 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
867 byte action = tape.pos[i].action[0];
868 int k, num_moves = 0;
872 if (action & joy_dir[k])
874 tape.pos[i + num_moves].action[0] = joy_dir[k];
876 tape.pos[i + num_moves].delay = 0;
885 tape.length += num_moves;
895 if (i != tape.length)
896 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
898 tape.length_seconds = GetTapeLength();
901 void SaveTape(int level_nr)
904 char *filename = getTapeFilename(level_nr);
906 boolean new_tape = TRUE;
907 byte store_participating_players;
908 int num_participating_players;
910 InitTapeDirectory(leveldir_current->filename);
912 /* if a tape still exists, ask to overwrite it */
913 if (access(filename, F_OK) == 0)
916 if (!Request("Replace old tape ?", REQ_ASK))
920 /* count number of players and set corresponding bits for compact storage */
921 store_participating_players = 0;
922 num_participating_players = 0;
923 for(i=0; i<MAX_PLAYERS; i++)
925 if (tape.player_participates[i])
927 num_participating_players++;
928 store_participating_players |= (1 << i);
932 if (!(file = fopen(filename, "w")))
934 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
938 fputs(TAPE_COOKIE, file); /* file identifier */
941 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
943 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
944 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
945 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
947 fputc(store_participating_players, file);
949 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
952 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
953 BYTE_ORDER_BIG_ENDIAN);
955 for(i=0; i<tape.length; i++)
959 for(j=0; j<MAX_PLAYERS; j++)
960 if (tape.player_participates[j])
961 fputc(tape.pos[i].action[j], file);
963 fputc(tape.pos[i].delay, file);
968 chmod(filename, TAPE_PERMS);
970 tape.changed = FALSE;
973 Request("tape saved !", REQ_CONFIRM);
976 void LoadScore(int level_nr)
979 char *filename = getScoreFilename(level_nr);
980 char cookie[MAX_LINE_LEN];
981 char line[MAX_LINE_LEN];
985 /* always start with reliable default values */
986 for(i=0; i<MAX_SCORE_ENTRIES; i++)
988 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
989 highscore[i].Score = 0;
992 if (!(file = fopen(filename, "r")))
995 /* check file identifier */
996 fgets(cookie, MAX_LINE_LEN, file);
997 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
998 cookie[strlen(cookie) - 1] = '\0';
1000 if (strcmp(cookie, SCORE_COOKIE) != 0)
1002 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1007 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1009 fscanf(file, "%d", &highscore[i].Score);
1010 fgets(line, MAX_LINE_LEN, file);
1012 if (line[strlen(line) - 1] == '\n')
1013 line[strlen(line) - 1] = '\0';
1015 for (line_ptr = line; *line_ptr; line_ptr++)
1017 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1019 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1020 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1029 void SaveScore(int level_nr)
1032 char *filename = getScoreFilename(level_nr);
1035 InitScoreDirectory(leveldir_current->filename);
1037 if (!(file = fopen(filename, "w")))
1039 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1043 fprintf(file, "%s\n\n", SCORE_COOKIE);
1045 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1046 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1050 chmod(filename, SCORE_PERMS);
1053 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1054 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1055 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1056 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1057 #define TOKEN_STR_PLAYER_PREFIX "player_"
1059 #define TOKEN_VALUE_POSITION 30
1062 #define SETUP_TOKEN_PLAYER_NAME 0
1063 #define SETUP_TOKEN_SOUND 1
1064 #define SETUP_TOKEN_SOUND_LOOPS 2
1065 #define SETUP_TOKEN_SOUND_MUSIC 3
1066 #define SETUP_TOKEN_SOUND_SIMPLE 4
1069 #define SETUP_TOKEN_TOONS 5
1070 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1073 #define SETUP_TOKEN_SCROLL_DELAY 5
1074 #define SETUP_TOKEN_SOFT_SCROLLING 6
1075 #define SETUP_TOKEN_FADING 7
1076 #define SETUP_TOKEN_AUTORECORD 8
1077 #define SETUP_TOKEN_QUICK_DOORS 9
1078 #define SETUP_TOKEN_TEAM_MODE 10
1079 #define SETUP_TOKEN_HANDICAP 11
1080 #define SETUP_TOKEN_TIME_LIMIT 12
1083 #define SETUP_TOKEN_USE_JOYSTICK 13
1084 #define SETUP_TOKEN_JOY_DEVICE_NAME 14
1085 #define SETUP_TOKEN_JOY_XLEFT 15
1086 #define SETUP_TOKEN_JOY_XMIDDLE 16
1087 #define SETUP_TOKEN_JOY_XRIGHT 17
1088 #define SETUP_TOKEN_JOY_YUPPER 18
1089 #define SETUP_TOKEN_JOY_YMIDDLE 19
1090 #define SETUP_TOKEN_JOY_YLOWER 20
1091 #define SETUP_TOKEN_JOY_SNAP 21
1092 #define SETUP_TOKEN_JOY_BOMB 22
1093 #define SETUP_TOKEN_KEY_LEFT 23
1094 #define SETUP_TOKEN_KEY_RIGHT 24
1095 #define SETUP_TOKEN_KEY_UP 25
1096 #define SETUP_TOKEN_KEY_DOWN 26
1097 #define SETUP_TOKEN_KEY_SNAP 27
1098 #define SETUP_TOKEN_KEY_BOMB 28
1100 /* level directory info */
1101 #define LEVELINFO_TOKEN_NAME 29
1102 #define LEVELINFO_TOKEN_NAME_SHORT 30
1103 #define LEVELINFO_TOKEN_NAME_SORTING 31
1104 #define LEVELINFO_TOKEN_AUTHOR 32
1105 #define LEVELINFO_TOKEN_IMPORTED_FROM 33
1106 #define LEVELINFO_TOKEN_LEVELS 34
1107 #define LEVELINFO_TOKEN_FIRST_LEVEL 35
1108 #define LEVELINFO_TOKEN_SORT_PRIORITY 36
1109 #define LEVELINFO_TOKEN_LEVEL_GROUP 37
1110 #define LEVELINFO_TOKEN_READONLY 38
1112 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1113 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_TIME_LIMIT
1115 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1116 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1118 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1119 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1121 #define TYPE_BOOLEAN 1
1122 #define TYPE_SWITCH 2
1124 #define TYPE_INTEGER 4
1125 #define TYPE_STRING 5
1127 static struct SetupInfo si;
1128 static struct SetupInputInfo sii;
1129 static struct LevelDirInfo ldi;
1138 { TYPE_STRING, &si.player_name, "player_name" },
1139 { TYPE_SWITCH, &si.sound, "sound" },
1140 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1141 { TYPE_SWITCH, &si.sound_music, "background_music" },
1142 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1145 { TYPE_SWITCH, &si.toons, "toons" },
1146 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1149 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1150 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1151 { TYPE_SWITCH, &si.fading, "screen_fading" },
1152 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1153 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1154 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1155 { TYPE_SWITCH, &si.handicap, "handicap" },
1156 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1159 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1160 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1161 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1162 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1163 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1164 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1165 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1166 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1167 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1168 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1169 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1170 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1171 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1172 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1173 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1174 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1176 /* level directory info */
1177 { TYPE_STRING, &ldi.name, "name" },
1178 { TYPE_STRING, &ldi.name_short, "name_short" },
1179 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1180 { TYPE_STRING, &ldi.author, "author" },
1181 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1182 { TYPE_INTEGER, &ldi.levels, "levels" },
1183 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1184 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1185 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1186 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1189 static char *string_tolower(char *s)
1191 static char s_lower[100];
1194 if (strlen(s) >= 100)
1199 for (i=0; i<strlen(s_lower); i++)
1200 s_lower[i] = tolower(s_lower[i]);
1205 static int get_string_integer_value(char *s)
1207 static char *number_text[][3] =
1209 { "0", "zero", "null", },
1210 { "1", "one", "first" },
1211 { "2", "two", "second" },
1212 { "3", "three", "third" },
1213 { "4", "four", "fourth" },
1214 { "5", "five", "fifth" },
1215 { "6", "six", "sixth" },
1216 { "7", "seven", "seventh" },
1217 { "8", "eight", "eighth" },
1218 { "9", "nine", "ninth" },
1219 { "10", "ten", "tenth" },
1220 { "11", "eleven", "eleventh" },
1221 { "12", "twelve", "twelfth" },
1226 for (i=0; i<13; i++)
1228 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1234 static boolean get_string_boolean_value(char *s)
1236 if (strcmp(string_tolower(s), "true") == 0 ||
1237 strcmp(string_tolower(s), "yes") == 0 ||
1238 strcmp(string_tolower(s), "on") == 0 ||
1239 get_string_integer_value(s) == 1)
1245 static char *getFormattedSetupEntry(char *token, char *value)
1248 static char entry[MAX_LINE_LEN];
1250 sprintf(entry, "%s:", token);
1251 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1255 strcat(entry, value);
1260 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1262 if (!setup_file_list)
1265 if (setup_file_list->token)
1266 free(setup_file_list->token);
1267 if (setup_file_list->value)
1268 free(setup_file_list->value);
1269 if (setup_file_list->next)
1270 freeSetupFileList(setup_file_list->next);
1271 free(setup_file_list);
1274 static struct SetupFileList *newSetupFileList(char *token, char *value)
1276 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1278 new->token = checked_malloc(strlen(token) + 1);
1279 strcpy(new->token, token);
1281 new->value = checked_malloc(strlen(value) + 1);
1282 strcpy(new->value, value);
1289 static char *getTokenValue(struct SetupFileList *setup_file_list,
1292 if (!setup_file_list)
1295 if (strcmp(setup_file_list->token, token) == 0)
1296 return setup_file_list->value;
1298 return getTokenValue(setup_file_list->next, token);
1301 static void setTokenValue(struct SetupFileList *setup_file_list,
1302 char *token, char *value)
1304 if (!setup_file_list)
1307 if (strcmp(setup_file_list->token, token) == 0)
1309 free(setup_file_list->value);
1310 setup_file_list->value = checked_malloc(strlen(value) + 1);
1311 strcpy(setup_file_list->value, value);
1313 else if (setup_file_list->next == NULL)
1314 setup_file_list->next = newSetupFileList(token, value);
1316 setTokenValue(setup_file_list->next, token, value);
1320 static void printSetupFileList(struct SetupFileList *setup_file_list)
1322 if (!setup_file_list)
1325 printf("token: '%s'\n", setup_file_list->token);
1326 printf("value: '%s'\n", setup_file_list->value);
1328 printSetupFileList(setup_file_list->next);
1332 static struct SetupFileList *loadSetupFileList(char *filename)
1335 char line[MAX_LINE_LEN];
1336 char *token, *value, *line_ptr;
1337 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1338 struct SetupFileList *first_valid_list_entry;
1342 if (!(file = fopen(filename, "r")))
1344 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1350 /* read next line of input file */
1351 if (!fgets(line, MAX_LINE_LEN, file))
1354 /* cut trailing comment or whitespace from input line */
1355 for (line_ptr = line; *line_ptr; line_ptr++)
1357 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1364 /* cut trailing whitespaces from input line */
1365 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1366 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1369 /* ignore empty lines */
1373 line_len = strlen(line);
1375 /* cut leading whitespaces from token */
1376 for (token = line; *token; token++)
1377 if (*token != ' ' && *token != '\t')
1380 /* find end of token */
1381 for (line_ptr = token; *line_ptr; line_ptr++)
1383 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1390 if (line_ptr < line + line_len)
1391 value = line_ptr + 1;
1395 /* cut leading whitespaces from value */
1396 for (; *value; value++)
1397 if (*value != ' ' && *value != '\t')
1400 if (*token && *value)
1401 setTokenValue(setup_file_list, token, value);
1406 first_valid_list_entry = setup_file_list->next;
1408 /* free empty list header */
1409 setup_file_list->next = NULL;
1410 freeSetupFileList(setup_file_list);
1412 if (first_valid_list_entry == NULL)
1413 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1415 return first_valid_list_entry;
1418 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1421 if (!setup_file_list)
1424 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1426 if (strcmp(setup_file_list->value, identifier) != 0)
1428 Error(ERR_WARN, "configuration file has wrong version");
1435 if (setup_file_list->next)
1436 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1439 Error(ERR_WARN, "configuration file has no version information");
1444 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1446 ldi->filename = NULL;
1447 ldi->fullpath = NULL;
1448 ldi->basepath = NULL;
1449 ldi->name = getStringCopy(ANONYMOUS_NAME);
1450 ldi->name_short = NULL;
1451 ldi->name_sorting = NULL;
1452 ldi->author = getStringCopy(ANONYMOUS_NAME);
1453 ldi->imported_from = NULL;
1455 ldi->first_level = 0;
1456 ldi->last_level = 0;
1457 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1458 ldi->level_group = FALSE;
1459 ldi->parent_link = FALSE;
1460 ldi->user_defined = FALSE;
1461 ldi->readonly = TRUE;
1463 ldi->class_desc = NULL;
1464 ldi->handicap_level = 0;
1466 ldi->cl_cursor = -1;
1468 ldi->node_parent = NULL;
1469 ldi->node_group = NULL;
1473 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1474 struct LevelDirInfo *parent)
1478 setLevelDirInfoToDefaults(ldi);
1482 /* first copy all values from the parent structure ... */
1485 /* ... then set all fields to default that cannot be inherited from parent.
1486 This is especially important for all those fields that can be set from
1487 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1488 calls 'free()' for all already set token values which requires that no
1489 other structure's pointer may point to them!
1492 ldi->filename = NULL;
1493 ldi->fullpath = NULL;
1494 ldi->basepath = NULL;
1495 ldi->name = getStringCopy(ANONYMOUS_NAME);
1496 ldi->name_short = NULL;
1497 ldi->name_sorting = NULL;
1498 ldi->author = getStringCopy(parent->author);
1499 ldi->imported_from = getStringCopy(parent->imported_from);
1501 ldi->level_group = FALSE;
1502 ldi->parent_link = FALSE;
1504 ldi->node_parent = parent;
1505 ldi->node_group = NULL;
1509 static void setSetupInfoToDefaults(struct SetupInfo *si)
1513 si->player_name = getStringCopy(getLoginName());
1516 si->sound_loops = TRUE;
1517 si->sound_music = TRUE;
1518 si->sound_simple = TRUE;
1520 si->double_buffering = TRUE;
1521 si->direct_draw = !si->double_buffering;
1522 si->scroll_delay = TRUE;
1523 si->soft_scrolling = TRUE;
1525 si->autorecord = TRUE;
1526 si->quick_doors = FALSE;
1527 si->team_mode = FALSE;
1528 si->handicap = TRUE;
1529 si->time_limit = TRUE;
1531 for (i=0; i<MAX_PLAYERS; i++)
1533 si->input[i].use_joystick = FALSE;
1534 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1535 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1536 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1537 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1538 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1539 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1540 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1541 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1542 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1543 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KEY_UNDEFINED);
1544 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINED);
1545 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KEY_UNDEFINED);
1546 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KEY_UNDEFINED);
1547 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KEY_UNDEFINED);
1548 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KEY_UNDEFINED);
1552 static void setSetupInfo(int token_nr, char *token_value)
1554 int token_type = token_info[token_nr].type;
1555 void *setup_value = token_info[token_nr].value;
1557 if (token_value == NULL)
1560 /* set setup field to corresponding token value */
1565 *(boolean *)setup_value = get_string_boolean_value(token_value);
1569 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1573 *(int *)setup_value = get_string_integer_value(token_value);
1577 if (*(char **)setup_value != NULL)
1578 free(*(char **)setup_value);
1579 *(char **)setup_value = getStringCopy(token_value);
1587 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1591 if (!setup_file_list)
1594 /* handle global setup values */
1596 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1597 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1600 /* handle player specific setup values */
1601 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1605 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1607 sii = setup.input[pnr];
1608 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1610 char full_token[100];
1612 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1613 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1615 setup.input[pnr] = sii;
1619 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1621 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1622 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1625 if (entry1->parent_link || entry2->parent_link)
1626 compare_result = (entry1->parent_link ? -1 : +1);
1627 else if (entry1->sort_priority == entry2->sort_priority)
1629 char *name1 = getStringToLower(entry1->name_sorting);
1630 char *name2 = getStringToLower(entry2->name_sorting);
1632 compare_result = strcmp(name1, name2);
1637 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1638 compare_result = entry1->sort_priority - entry2->sort_priority;
1640 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1642 return compare_result;
1645 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1647 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1649 setLevelDirInfoToDefaults(leveldir_new);
1651 leveldir_new->node_parent = node_parent;
1652 leveldir_new->parent_link = TRUE;
1654 leveldir_new->name = ".. (parent directory)";
1655 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1656 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1658 leveldir_new->filename = "..";
1659 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1661 leveldir_new->sort_priority = node_parent->sort_priority;
1662 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1664 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1667 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1668 struct LevelDirInfo *node_parent,
1669 char *level_directory)
1672 struct dirent *dir_entry;
1673 boolean valid_entry_found = FALSE;
1675 if ((dir = opendir(level_directory)) == NULL)
1677 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1681 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1683 struct SetupFileList *setup_file_list = NULL;
1684 struct stat file_status;
1685 char *directory_name = dir_entry->d_name;
1686 char *directory_path = getPath2(level_directory, directory_name);
1687 char *filename = NULL;
1689 /* skip entries for current and parent directory */
1690 if (strcmp(directory_name, ".") == 0 ||
1691 strcmp(directory_name, "..") == 0)
1693 free(directory_path);
1697 /* find out if directory entry is itself a directory */
1698 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1699 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1701 free(directory_path);
1705 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1706 setup_file_list = loadSetupFileList(filename);
1708 if (setup_file_list)
1710 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1713 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1714 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1716 /* set all structure fields according to the token/value pairs */
1717 ldi = *leveldir_new;
1718 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1719 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1720 *leveldir_new = ldi;
1722 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1724 if (leveldir_new->name_short == NULL)
1725 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1727 if (leveldir_new->name_sorting == NULL)
1728 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1730 leveldir_new->filename = getStringCopy(directory_name);
1732 if (node_parent == NULL) /* top level group */
1734 leveldir_new->basepath = level_directory;
1735 leveldir_new->fullpath = leveldir_new->filename;
1737 else /* sub level group */
1739 leveldir_new->basepath = node_parent->basepath;
1740 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1744 if (leveldir_new->levels < 1)
1745 leveldir_new->levels = 1;
1747 leveldir_new->last_level =
1748 leveldir_new->first_level + leveldir_new->levels - 1;
1750 leveldir_new->user_defined =
1751 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1753 leveldir_new->color = LEVELCOLOR(leveldir_new);
1754 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1756 leveldir_new->handicap_level = /* set handicap to default value */
1757 (leveldir_new->user_defined ?
1758 leveldir_new->last_level :
1759 leveldir_new->first_level);
1761 pushLevelDirInfo(node_first, leveldir_new);
1763 freeSetupFileList(setup_file_list);
1764 valid_entry_found = TRUE;
1766 if (leveldir_new->level_group)
1768 /* create node to link back to current level directory */
1769 createParentLevelDirNode(leveldir_new);
1771 /* step into sub-directory and look for more level series */
1772 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1773 leveldir_new, directory_path);
1777 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1779 free(directory_path);
1785 if (!valid_entry_found)
1786 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1790 void LoadLevelInfo()
1792 InitUserLevelDirectory(getLoginName());
1794 DrawInitText("Loading level series:", 120, FC_GREEN);
1796 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1797 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1799 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1801 if (leveldir_first == NULL)
1802 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1804 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1807 dumpLevelDirInfo(leveldir_first, 0);
1811 static void SaveUserLevelInfo()
1817 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1819 if (!(file = fopen(filename, "w")))
1821 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1826 /* always start with reliable default values */
1827 setLevelDirInfoToDefaults(&ldi);
1829 ldi.name = getLoginName();
1830 ldi.author = getRealName();
1832 ldi.first_level = 1;
1833 ldi.sort_priority = LEVELCLASS_USER_START;
1834 ldi.readonly = FALSE;
1836 fprintf(file, "%s\n\n",
1837 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1839 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1840 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1841 i != LEVELINFO_TOKEN_NAME_SORTING &&
1842 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1843 fprintf(file, "%s\n", getSetupLine("", i));
1848 chmod(filename, SETUP_PERMS);
1854 struct SetupFileList *setup_file_list = NULL;
1856 /* always start with reliable default values */
1857 setSetupInfoToDefaults(&setup);
1859 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1861 setup_file_list = loadSetupFileList(filename);
1863 if (setup_file_list)
1865 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1866 decodeSetupFileList(setup_file_list);
1868 setup.direct_draw = !setup.double_buffering;
1870 freeSetupFileList(setup_file_list);
1872 /* needed to work around problems with fixed length strings */
1873 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1874 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1875 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1877 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1879 strcpy(new_name, setup.player_name);
1880 free(setup.player_name);
1881 setup.player_name = new_name;
1885 Error(ERR_WARN, "using default setup values");
1890 static char *getSetupLine(char *prefix, int token_nr)
1893 static char entry[MAX_LINE_LEN];
1894 int token_type = token_info[token_nr].type;
1895 void *setup_value = token_info[token_nr].value;
1896 char *token_text = token_info[token_nr].text;
1898 /* start with the prefix, token and some spaces to format output line */
1899 sprintf(entry, "%s%s:", prefix, token_text);
1900 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1903 /* continue with the token's value (which can have different types) */
1907 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1911 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1916 Key key = *(Key *)setup_value;
1917 char *keyname = getKeyNameFromKey(key);
1919 strcat(entry, getX11KeyNameFromKey(key));
1920 for (i=strlen(entry); i<50; i++)
1923 /* add comment, if useful */
1924 if (strcmp(keyname, "(undefined)") != 0 &&
1925 strcmp(keyname, "(unknown)") != 0)
1927 strcat(entry, "# ");
1928 strcat(entry, keyname);
1935 char buffer[MAX_LINE_LEN];
1937 sprintf(buffer, "%d", *(int *)setup_value);
1938 strcat(entry, buffer);
1943 strcat(entry, *(char **)setup_value);
1959 InitUserDataDirectory();
1961 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1963 if (!(file = fopen(filename, "w")))
1965 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1970 fprintf(file, "%s\n",
1971 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1972 fprintf(file, "\n");
1974 /* handle global setup values */
1976 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1978 fprintf(file, "%s\n", getSetupLine("", i));
1980 /* just to make things nicer :) */
1981 if (i == SETUP_TOKEN_PLAYER_NAME)
1982 fprintf(file, "\n");
1985 /* handle player specific setup values */
1986 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1990 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1991 fprintf(file, "\n");
1993 sii = setup.input[pnr];
1994 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1995 fprintf(file, "%s\n", getSetupLine(prefix, i));
2001 chmod(filename, SETUP_PERMS);
2004 void LoadLevelSetup_LastSeries()
2007 struct SetupFileList *level_setup_list = NULL;
2009 /* always start with reliable default values */
2010 leveldir_current = leveldir_first;
2012 /* ----------------------------------------------------------------------- */
2013 /* ~/.rocksndiamonds/levelsetup.conf */
2014 /* ----------------------------------------------------------------------- */
2016 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2018 if ((level_setup_list = loadSetupFileList(filename)))
2020 char *last_level_series =
2021 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2023 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2024 if (leveldir_current == NULL)
2025 leveldir_current = leveldir_first;
2027 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2029 freeSetupFileList(level_setup_list);
2032 Error(ERR_WARN, "using default setup values");
2037 void SaveLevelSetup_LastSeries()
2040 char *level_subdir = leveldir_current->filename;
2043 /* ----------------------------------------------------------------------- */
2044 /* ~/.rocksndiamonds/levelsetup.conf */
2045 /* ----------------------------------------------------------------------- */
2047 InitUserDataDirectory();
2049 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2051 if (!(file = fopen(filename, "w")))
2053 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2058 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2059 LEVELSETUP_COOKIE));
2060 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2066 chmod(filename, SETUP_PERMS);
2069 static void checkSeriesInfo()
2071 static char *level_directory = NULL;
2073 struct dirent *dir_entry;
2075 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2077 level_directory = getPath2((leveldir_current->user_defined ?
2078 getUserLevelDir("") :
2079 options.level_directory),
2080 leveldir_current->filename);
2082 if ((dir = opendir(level_directory)) == NULL)
2084 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2088 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2090 if (strlen(dir_entry->d_name) > 4 &&
2091 dir_entry->d_name[3] == '.' &&
2092 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2094 char levelnum_str[4];
2097 strncpy(levelnum_str, dir_entry->d_name, 3);
2098 levelnum_str[3] = '\0';
2100 levelnum_value = atoi(levelnum_str);
2102 if (levelnum_value < leveldir_current->first_level)
2104 Error(ERR_WARN, "additional level %d found", levelnum_value);
2105 leveldir_current->first_level = levelnum_value;
2107 else if (levelnum_value > leveldir_current->last_level)
2109 Error(ERR_WARN, "additional level %d found", levelnum_value);
2110 leveldir_current->last_level = levelnum_value;
2118 void LoadLevelSetup_SeriesInfo()
2121 struct SetupFileList *level_setup_list = NULL;
2122 char *level_subdir = leveldir_current->filename;
2124 /* always start with reliable default values */
2125 level_nr = leveldir_current->first_level;
2127 checkSeriesInfo(leveldir_current);
2129 /* ----------------------------------------------------------------------- */
2130 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2131 /* ----------------------------------------------------------------------- */
2133 level_subdir = leveldir_current->filename;
2135 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2137 if ((level_setup_list = loadSetupFileList(filename)))
2141 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2145 level_nr = atoi(token_value);
2147 if (level_nr < leveldir_current->first_level)
2148 level_nr = leveldir_current->first_level;
2149 if (level_nr > leveldir_current->last_level)
2150 level_nr = leveldir_current->last_level;
2153 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2157 int level_nr = atoi(token_value);
2159 if (level_nr < leveldir_current->first_level)
2160 level_nr = leveldir_current->first_level;
2161 if (level_nr > leveldir_current->last_level + 1)
2162 level_nr = leveldir_current->last_level;
2164 if (leveldir_current->user_defined)
2165 level_nr = leveldir_current->last_level;
2167 leveldir_current->handicap_level = level_nr;
2170 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2172 freeSetupFileList(level_setup_list);
2175 Error(ERR_WARN, "using default setup values");
2180 void SaveLevelSetup_SeriesInfo()
2183 char *level_subdir = leveldir_current->filename;
2184 char *level_nr_str = int2str(level_nr, 0);
2185 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2188 /* ----------------------------------------------------------------------- */
2189 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2190 /* ----------------------------------------------------------------------- */
2192 InitLevelSetupDirectory(level_subdir);
2194 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2196 if (!(file = fopen(filename, "w")))
2198 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2203 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2204 LEVELSETUP_COOKIE));
2205 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2207 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2208 handicap_level_str));
2213 chmod(filename, SETUP_PERMS);
2216 #if defined(MSDOS) || defined(WIN32)
2217 void initErrorFile()
2221 InitUserDataDirectory();
2223 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2228 FILE *openErrorFile()
2233 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2234 error_file = fopen(filename, "a");
2240 void dumpErrorFile()
2245 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2246 error_file = fopen(filename, "r");
2249 if (error_file != NULL)
2251 while (!feof(error_file))
2252 fputc(fgetc(error_file), stderr);