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
1081 #define SETUP_TOKEN_FULLSCREEN 13
1084 #define SETUP_TOKEN_USE_JOYSTICK 14
1085 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1086 #define SETUP_TOKEN_JOY_XLEFT 16
1087 #define SETUP_TOKEN_JOY_XMIDDLE 17
1088 #define SETUP_TOKEN_JOY_XRIGHT 18
1089 #define SETUP_TOKEN_JOY_YUPPER 19
1090 #define SETUP_TOKEN_JOY_YMIDDLE 20
1091 #define SETUP_TOKEN_JOY_YLOWER 21
1092 #define SETUP_TOKEN_JOY_SNAP 22
1093 #define SETUP_TOKEN_JOY_BOMB 23
1094 #define SETUP_TOKEN_KEY_LEFT 24
1095 #define SETUP_TOKEN_KEY_RIGHT 25
1096 #define SETUP_TOKEN_KEY_UP 26
1097 #define SETUP_TOKEN_KEY_DOWN 27
1098 #define SETUP_TOKEN_KEY_SNAP 28
1099 #define SETUP_TOKEN_KEY_BOMB 29
1101 /* level directory info */
1102 #define LEVELINFO_TOKEN_NAME 30
1103 #define LEVELINFO_TOKEN_NAME_SHORT 31
1104 #define LEVELINFO_TOKEN_NAME_SORTING 32
1105 #define LEVELINFO_TOKEN_AUTHOR 33
1106 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1107 #define LEVELINFO_TOKEN_LEVELS 35
1108 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1109 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1110 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1111 #define LEVELINFO_TOKEN_READONLY 39
1113 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1114 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1116 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1117 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1119 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1120 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1122 #define TYPE_BOOLEAN 1
1123 #define TYPE_SWITCH 2
1125 #define TYPE_INTEGER 4
1126 #define TYPE_STRING 5
1128 static struct SetupInfo si;
1129 static struct SetupInputInfo sii;
1130 static struct LevelDirInfo ldi;
1139 { TYPE_STRING, &si.player_name, "player_name" },
1140 { TYPE_SWITCH, &si.sound, "sound" },
1141 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1142 { TYPE_SWITCH, &si.sound_music, "background_music" },
1143 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1146 { TYPE_SWITCH, &si.toons, "toons" },
1147 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1150 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1151 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1152 { TYPE_SWITCH, &si.fading, "screen_fading" },
1153 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1154 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1155 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1156 { TYPE_SWITCH, &si.handicap, "handicap" },
1157 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1158 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1161 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1162 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1163 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1164 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1165 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1166 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1167 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1168 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1169 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1170 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1171 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1172 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1173 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1174 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1175 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1176 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1178 /* level directory info */
1179 { TYPE_STRING, &ldi.name, "name" },
1180 { TYPE_STRING, &ldi.name_short, "name_short" },
1181 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1182 { TYPE_STRING, &ldi.author, "author" },
1183 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1184 { TYPE_INTEGER, &ldi.levels, "levels" },
1185 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1186 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1187 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1188 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1191 static char *string_tolower(char *s)
1193 static char s_lower[100];
1196 if (strlen(s) >= 100)
1201 for (i=0; i<strlen(s_lower); i++)
1202 s_lower[i] = tolower(s_lower[i]);
1207 static int get_string_integer_value(char *s)
1209 static char *number_text[][3] =
1211 { "0", "zero", "null", },
1212 { "1", "one", "first" },
1213 { "2", "two", "second" },
1214 { "3", "three", "third" },
1215 { "4", "four", "fourth" },
1216 { "5", "five", "fifth" },
1217 { "6", "six", "sixth" },
1218 { "7", "seven", "seventh" },
1219 { "8", "eight", "eighth" },
1220 { "9", "nine", "ninth" },
1221 { "10", "ten", "tenth" },
1222 { "11", "eleven", "eleventh" },
1223 { "12", "twelve", "twelfth" },
1228 for (i=0; i<13; i++)
1230 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1236 static boolean get_string_boolean_value(char *s)
1238 if (strcmp(string_tolower(s), "true") == 0 ||
1239 strcmp(string_tolower(s), "yes") == 0 ||
1240 strcmp(string_tolower(s), "on") == 0 ||
1241 get_string_integer_value(s) == 1)
1247 static char *getFormattedSetupEntry(char *token, char *value)
1250 static char entry[MAX_LINE_LEN];
1252 sprintf(entry, "%s:", token);
1253 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1257 strcat(entry, value);
1262 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1264 if (!setup_file_list)
1267 if (setup_file_list->token)
1268 free(setup_file_list->token);
1269 if (setup_file_list->value)
1270 free(setup_file_list->value);
1271 if (setup_file_list->next)
1272 freeSetupFileList(setup_file_list->next);
1273 free(setup_file_list);
1276 static struct SetupFileList *newSetupFileList(char *token, char *value)
1278 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1280 new->token = checked_malloc(strlen(token) + 1);
1281 strcpy(new->token, token);
1283 new->value = checked_malloc(strlen(value) + 1);
1284 strcpy(new->value, value);
1291 static char *getTokenValue(struct SetupFileList *setup_file_list,
1294 if (!setup_file_list)
1297 if (strcmp(setup_file_list->token, token) == 0)
1298 return setup_file_list->value;
1300 return getTokenValue(setup_file_list->next, token);
1303 static void setTokenValue(struct SetupFileList *setup_file_list,
1304 char *token, char *value)
1306 if (!setup_file_list)
1309 if (strcmp(setup_file_list->token, token) == 0)
1311 free(setup_file_list->value);
1312 setup_file_list->value = checked_malloc(strlen(value) + 1);
1313 strcpy(setup_file_list->value, value);
1315 else if (setup_file_list->next == NULL)
1316 setup_file_list->next = newSetupFileList(token, value);
1318 setTokenValue(setup_file_list->next, token, value);
1322 static void printSetupFileList(struct SetupFileList *setup_file_list)
1324 if (!setup_file_list)
1327 printf("token: '%s'\n", setup_file_list->token);
1328 printf("value: '%s'\n", setup_file_list->value);
1330 printSetupFileList(setup_file_list->next);
1334 static struct SetupFileList *loadSetupFileList(char *filename)
1337 char line[MAX_LINE_LEN];
1338 char *token, *value, *line_ptr;
1339 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1340 struct SetupFileList *first_valid_list_entry;
1344 if (!(file = fopen(filename, "r")))
1346 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1352 /* read next line of input file */
1353 if (!fgets(line, MAX_LINE_LEN, file))
1356 /* cut trailing comment or whitespace from input line */
1357 for (line_ptr = line; *line_ptr; line_ptr++)
1359 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1366 /* cut trailing whitespaces from input line */
1367 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1368 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1371 /* ignore empty lines */
1375 line_len = strlen(line);
1377 /* cut leading whitespaces from token */
1378 for (token = line; *token; token++)
1379 if (*token != ' ' && *token != '\t')
1382 /* find end of token */
1383 for (line_ptr = token; *line_ptr; line_ptr++)
1385 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1392 if (line_ptr < line + line_len)
1393 value = line_ptr + 1;
1397 /* cut leading whitespaces from value */
1398 for (; *value; value++)
1399 if (*value != ' ' && *value != '\t')
1402 if (*token && *value)
1403 setTokenValue(setup_file_list, token, value);
1408 first_valid_list_entry = setup_file_list->next;
1410 /* free empty list header */
1411 setup_file_list->next = NULL;
1412 freeSetupFileList(setup_file_list);
1414 if (first_valid_list_entry == NULL)
1415 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1417 return first_valid_list_entry;
1420 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1423 if (!setup_file_list)
1426 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1428 if (strcmp(setup_file_list->value, identifier) != 0)
1430 Error(ERR_WARN, "configuration file has wrong version");
1437 if (setup_file_list->next)
1438 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1441 Error(ERR_WARN, "configuration file has no version information");
1446 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1448 ldi->filename = NULL;
1449 ldi->fullpath = NULL;
1450 ldi->basepath = NULL;
1451 ldi->name = getStringCopy(ANONYMOUS_NAME);
1452 ldi->name_short = NULL;
1453 ldi->name_sorting = NULL;
1454 ldi->author = getStringCopy(ANONYMOUS_NAME);
1455 ldi->imported_from = NULL;
1457 ldi->first_level = 0;
1458 ldi->last_level = 0;
1459 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1460 ldi->level_group = FALSE;
1461 ldi->parent_link = FALSE;
1462 ldi->user_defined = FALSE;
1463 ldi->readonly = TRUE;
1465 ldi->class_desc = NULL;
1466 ldi->handicap_level = 0;
1468 ldi->cl_cursor = -1;
1470 ldi->node_parent = NULL;
1471 ldi->node_group = NULL;
1475 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1476 struct LevelDirInfo *parent)
1480 setLevelDirInfoToDefaults(ldi);
1484 /* first copy all values from the parent structure ... */
1487 /* ... then set all fields to default that cannot be inherited from parent.
1488 This is especially important for all those fields that can be set from
1489 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1490 calls 'free()' for all already set token values which requires that no
1491 other structure's pointer may point to them!
1494 ldi->filename = NULL;
1495 ldi->fullpath = NULL;
1496 ldi->basepath = NULL;
1497 ldi->name = getStringCopy(ANONYMOUS_NAME);
1498 ldi->name_short = NULL;
1499 ldi->name_sorting = NULL;
1500 ldi->author = getStringCopy(parent->author);
1501 ldi->imported_from = getStringCopy(parent->imported_from);
1503 ldi->level_group = FALSE;
1504 ldi->parent_link = FALSE;
1506 ldi->node_parent = parent;
1507 ldi->node_group = NULL;
1511 static void setSetupInfoToDefaults(struct SetupInfo *si)
1515 si->player_name = getStringCopy(getLoginName());
1518 si->sound_loops = TRUE;
1519 si->sound_music = TRUE;
1520 si->sound_simple = TRUE;
1522 si->double_buffering = TRUE;
1523 si->direct_draw = !si->double_buffering;
1524 si->scroll_delay = TRUE;
1525 si->soft_scrolling = TRUE;
1527 si->autorecord = TRUE;
1528 si->quick_doors = FALSE;
1529 si->team_mode = FALSE;
1530 si->handicap = TRUE;
1531 si->time_limit = TRUE;
1532 si->fullscreen = FALSE;
1534 for (i=0; i<MAX_PLAYERS; i++)
1536 si->input[i].use_joystick = FALSE;
1537 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1538 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1539 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1540 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1541 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1542 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1543 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1544 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1545 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1546 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KEY_UNDEFINED);
1547 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINED);
1548 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KEY_UNDEFINED);
1549 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KEY_UNDEFINED);
1550 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KEY_UNDEFINED);
1551 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KEY_UNDEFINED);
1555 static void setSetupInfo(int token_nr, char *token_value)
1557 int token_type = token_info[token_nr].type;
1558 void *setup_value = token_info[token_nr].value;
1560 if (token_value == NULL)
1563 /* set setup field to corresponding token value */
1568 *(boolean *)setup_value = get_string_boolean_value(token_value);
1572 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1576 *(int *)setup_value = get_string_integer_value(token_value);
1580 if (*(char **)setup_value != NULL)
1581 free(*(char **)setup_value);
1582 *(char **)setup_value = getStringCopy(token_value);
1590 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1594 if (!setup_file_list)
1597 /* handle global setup values */
1599 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1600 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1603 /* handle player specific setup values */
1604 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1608 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1610 sii = setup.input[pnr];
1611 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1613 char full_token[100];
1615 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1616 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1618 setup.input[pnr] = sii;
1622 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1624 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1625 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1628 if (entry1->parent_link || entry2->parent_link)
1629 compare_result = (entry1->parent_link ? -1 : +1);
1630 else if (entry1->sort_priority == entry2->sort_priority)
1632 char *name1 = getStringToLower(entry1->name_sorting);
1633 char *name2 = getStringToLower(entry2->name_sorting);
1635 compare_result = strcmp(name1, name2);
1640 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1641 compare_result = entry1->sort_priority - entry2->sort_priority;
1643 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1645 return compare_result;
1648 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1650 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1652 setLevelDirInfoToDefaults(leveldir_new);
1654 leveldir_new->node_parent = node_parent;
1655 leveldir_new->parent_link = TRUE;
1657 leveldir_new->name = ".. (parent directory)";
1658 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1659 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1661 leveldir_new->filename = "..";
1662 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1664 leveldir_new->sort_priority = node_parent->sort_priority;
1665 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1667 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1670 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1671 struct LevelDirInfo *node_parent,
1672 char *level_directory)
1675 struct dirent *dir_entry;
1676 boolean valid_entry_found = FALSE;
1678 if ((dir = opendir(level_directory)) == NULL)
1680 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1684 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1686 struct SetupFileList *setup_file_list = NULL;
1687 struct stat file_status;
1688 char *directory_name = dir_entry->d_name;
1689 char *directory_path = getPath2(level_directory, directory_name);
1690 char *filename = NULL;
1692 /* skip entries for current and parent directory */
1693 if (strcmp(directory_name, ".") == 0 ||
1694 strcmp(directory_name, "..") == 0)
1696 free(directory_path);
1700 /* find out if directory entry is itself a directory */
1701 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1702 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1704 free(directory_path);
1708 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1709 setup_file_list = loadSetupFileList(filename);
1711 if (setup_file_list)
1713 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1716 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1717 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1719 /* set all structure fields according to the token/value pairs */
1720 ldi = *leveldir_new;
1721 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1722 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1723 *leveldir_new = ldi;
1725 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1727 if (leveldir_new->name_short == NULL)
1728 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1730 if (leveldir_new->name_sorting == NULL)
1731 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1733 leveldir_new->filename = getStringCopy(directory_name);
1735 if (node_parent == NULL) /* top level group */
1737 leveldir_new->basepath = level_directory;
1738 leveldir_new->fullpath = leveldir_new->filename;
1740 else /* sub level group */
1742 leveldir_new->basepath = node_parent->basepath;
1743 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1747 if (leveldir_new->levels < 1)
1748 leveldir_new->levels = 1;
1750 leveldir_new->last_level =
1751 leveldir_new->first_level + leveldir_new->levels - 1;
1753 leveldir_new->user_defined =
1754 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1756 leveldir_new->color = LEVELCOLOR(leveldir_new);
1757 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1759 leveldir_new->handicap_level = /* set handicap to default value */
1760 (leveldir_new->user_defined ?
1761 leveldir_new->last_level :
1762 leveldir_new->first_level);
1764 pushLevelDirInfo(node_first, leveldir_new);
1766 freeSetupFileList(setup_file_list);
1767 valid_entry_found = TRUE;
1769 if (leveldir_new->level_group)
1771 /* create node to link back to current level directory */
1772 createParentLevelDirNode(leveldir_new);
1774 /* step into sub-directory and look for more level series */
1775 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1776 leveldir_new, directory_path);
1780 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1782 free(directory_path);
1788 if (!valid_entry_found)
1789 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1793 void LoadLevelInfo()
1795 InitUserLevelDirectory(getLoginName());
1797 DrawInitText("Loading level series:", 120, FC_GREEN);
1799 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1800 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1802 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1804 if (leveldir_first == NULL)
1805 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1807 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1810 dumpLevelDirInfo(leveldir_first, 0);
1814 static void SaveUserLevelInfo()
1820 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1822 if (!(file = fopen(filename, "w")))
1824 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1829 /* always start with reliable default values */
1830 setLevelDirInfoToDefaults(&ldi);
1832 ldi.name = getLoginName();
1833 ldi.author = getRealName();
1835 ldi.first_level = 1;
1836 ldi.sort_priority = LEVELCLASS_USER_START;
1837 ldi.readonly = FALSE;
1839 fprintf(file, "%s\n\n",
1840 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1842 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1843 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1844 i != LEVELINFO_TOKEN_NAME_SORTING &&
1845 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1846 fprintf(file, "%s\n", getSetupLine("", i));
1851 chmod(filename, SETUP_PERMS);
1857 struct SetupFileList *setup_file_list = NULL;
1859 /* always start with reliable default values */
1860 setSetupInfoToDefaults(&setup);
1862 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1864 setup_file_list = loadSetupFileList(filename);
1866 if (setup_file_list)
1868 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1869 decodeSetupFileList(setup_file_list);
1871 setup.direct_draw = !setup.double_buffering;
1873 freeSetupFileList(setup_file_list);
1875 /* needed to work around problems with fixed length strings */
1876 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1877 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1878 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1880 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1882 strcpy(new_name, setup.player_name);
1883 free(setup.player_name);
1884 setup.player_name = new_name;
1888 Error(ERR_WARN, "using default setup values");
1893 static char *getSetupLine(char *prefix, int token_nr)
1896 static char entry[MAX_LINE_LEN];
1897 int token_type = token_info[token_nr].type;
1898 void *setup_value = token_info[token_nr].value;
1899 char *token_text = token_info[token_nr].text;
1901 /* start with the prefix, token and some spaces to format output line */
1902 sprintf(entry, "%s%s:", prefix, token_text);
1903 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1906 /* continue with the token's value (which can have different types) */
1910 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1914 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1919 Key key = *(Key *)setup_value;
1920 char *keyname = getKeyNameFromKey(key);
1922 strcat(entry, getX11KeyNameFromKey(key));
1923 for (i=strlen(entry); i<50; i++)
1926 /* add comment, if useful */
1927 if (strcmp(keyname, "(undefined)") != 0 &&
1928 strcmp(keyname, "(unknown)") != 0)
1930 strcat(entry, "# ");
1931 strcat(entry, keyname);
1938 char buffer[MAX_LINE_LEN];
1940 sprintf(buffer, "%d", *(int *)setup_value);
1941 strcat(entry, buffer);
1946 strcat(entry, *(char **)setup_value);
1962 InitUserDataDirectory();
1964 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1966 if (!(file = fopen(filename, "w")))
1968 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1973 fprintf(file, "%s\n",
1974 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1975 fprintf(file, "\n");
1977 /* handle global setup values */
1979 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1981 fprintf(file, "%s\n", getSetupLine("", i));
1983 /* just to make things nicer :) */
1984 if (i == SETUP_TOKEN_PLAYER_NAME)
1985 fprintf(file, "\n");
1988 /* handle player specific setup values */
1989 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1993 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1994 fprintf(file, "\n");
1996 sii = setup.input[pnr];
1997 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1998 fprintf(file, "%s\n", getSetupLine(prefix, i));
2004 chmod(filename, SETUP_PERMS);
2007 void LoadLevelSetup_LastSeries()
2010 struct SetupFileList *level_setup_list = NULL;
2012 /* always start with reliable default values */
2013 leveldir_current = leveldir_first;
2015 /* ----------------------------------------------------------------------- */
2016 /* ~/.rocksndiamonds/levelsetup.conf */
2017 /* ----------------------------------------------------------------------- */
2019 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2021 if ((level_setup_list = loadSetupFileList(filename)))
2023 char *last_level_series =
2024 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2026 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2027 if (leveldir_current == NULL)
2028 leveldir_current = leveldir_first;
2030 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2032 freeSetupFileList(level_setup_list);
2035 Error(ERR_WARN, "using default setup values");
2040 void SaveLevelSetup_LastSeries()
2043 char *level_subdir = leveldir_current->filename;
2046 /* ----------------------------------------------------------------------- */
2047 /* ~/.rocksndiamonds/levelsetup.conf */
2048 /* ----------------------------------------------------------------------- */
2050 InitUserDataDirectory();
2052 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2054 if (!(file = fopen(filename, "w")))
2056 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2061 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2062 LEVELSETUP_COOKIE));
2063 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2069 chmod(filename, SETUP_PERMS);
2072 static void checkSeriesInfo()
2074 static char *level_directory = NULL;
2076 struct dirent *dir_entry;
2078 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2080 level_directory = getPath2((leveldir_current->user_defined ?
2081 getUserLevelDir("") :
2082 options.level_directory),
2083 leveldir_current->fullpath);
2085 if ((dir = opendir(level_directory)) == NULL)
2087 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2091 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2093 if (strlen(dir_entry->d_name) > 4 &&
2094 dir_entry->d_name[3] == '.' &&
2095 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2097 char levelnum_str[4];
2100 strncpy(levelnum_str, dir_entry->d_name, 3);
2101 levelnum_str[3] = '\0';
2103 levelnum_value = atoi(levelnum_str);
2105 if (levelnum_value < leveldir_current->first_level)
2107 Error(ERR_WARN, "additional level %d found", levelnum_value);
2108 leveldir_current->first_level = levelnum_value;
2110 else if (levelnum_value > leveldir_current->last_level)
2112 Error(ERR_WARN, "additional level %d found", levelnum_value);
2113 leveldir_current->last_level = levelnum_value;
2121 void LoadLevelSetup_SeriesInfo()
2124 struct SetupFileList *level_setup_list = NULL;
2125 char *level_subdir = leveldir_current->filename;
2127 /* always start with reliable default values */
2128 level_nr = leveldir_current->first_level;
2130 checkSeriesInfo(leveldir_current);
2132 /* ----------------------------------------------------------------------- */
2133 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2134 /* ----------------------------------------------------------------------- */
2136 level_subdir = leveldir_current->filename;
2138 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2140 if ((level_setup_list = loadSetupFileList(filename)))
2144 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2148 level_nr = atoi(token_value);
2150 if (level_nr < leveldir_current->first_level)
2151 level_nr = leveldir_current->first_level;
2152 if (level_nr > leveldir_current->last_level)
2153 level_nr = leveldir_current->last_level;
2156 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2160 int level_nr = atoi(token_value);
2162 if (level_nr < leveldir_current->first_level)
2163 level_nr = leveldir_current->first_level;
2164 if (level_nr > leveldir_current->last_level + 1)
2165 level_nr = leveldir_current->last_level;
2167 if (leveldir_current->user_defined)
2168 level_nr = leveldir_current->last_level;
2170 leveldir_current->handicap_level = level_nr;
2173 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2175 freeSetupFileList(level_setup_list);
2178 Error(ERR_WARN, "using default setup values");
2183 void SaveLevelSetup_SeriesInfo()
2186 char *level_subdir = leveldir_current->filename;
2187 char *level_nr_str = int2str(level_nr, 0);
2188 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2191 /* ----------------------------------------------------------------------- */
2192 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2193 /* ----------------------------------------------------------------------- */
2195 InitLevelSetupDirectory(level_subdir);
2197 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2199 if (!(file = fopen(filename, "w")))
2201 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2206 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2207 LEVELSETUP_COOKIE));
2208 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2210 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2211 handicap_level_str));
2216 chmod(filename, SETUP_PERMS);
2219 #if defined(MSDOS) || defined(WIN32)
2220 void initErrorFile()
2224 InitUserDataDirectory();
2226 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2231 FILE *openErrorFile()
2236 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2237 error_file = fopen(filename, "a");
2243 void dumpErrorFile()
2248 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2249 error_file = fopen(filename, "r");
2252 if (error_file != NULL)
2254 while (!feof(error_file))
2255 fputc(fgetc(error_file), stderr);