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 ***********************************************************/
25 #define MAX_FILENAME_LEN 256 /* maximal filename length */
26 #define MAX_LINE_LEN 1000 /* maximal input line length */
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
29 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
30 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
31 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
32 #define FILE_VERSION_1_0 10 /* 1.0 file version (old) */
33 #define FILE_VERSION_1_2 12 /* 1.2 file version (still in use) */
34 #define FILE_VERSION_1_4 14 /* 1.4 file version (new) */
36 /* file identifier strings */
37 #define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
38 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
39 #define TAPE_COOKIE "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
40 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
41 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
42 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
43 /* old file identifiers for backward compatibility */
44 #define LEVEL_COOKIE_10 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
45 #define LEVEL_COOKIE_12 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
46 #define TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
48 /* file names and filename extensions */
50 #define USERDATA_DIRECTORY ".rocksndiamonds"
51 #define LEVELSETUP_DIRECTORY "levelsetup"
52 #define SETUP_FILENAME "setup.conf"
53 #define LEVELSETUP_FILENAME "levelsetup.conf"
54 #define LEVELINFO_FILENAME "levelinfo.conf"
55 #define LEVELFILE_EXTENSION "level"
56 #define TAPEFILE_EXTENSION "tape"
57 #define SCOREFILE_EXTENSION "score"
59 #define USERDATA_DIRECTORY "userdata"
60 #define LEVELSETUP_DIRECTORY "lvlsetup"
61 #define SETUP_FILENAME "setup.cnf"
62 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
63 #define LEVELINFO_FILENAME "lvlinfo.cnf"
64 #define LEVELFILE_EXTENSION "lvl"
65 #define TAPEFILE_EXTENSION "tap"
66 #define SCOREFILE_EXTENSION "sco"
67 #define ERROR_FILENAME "error.out"
70 /* file permissions for newly written files */
71 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
72 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
73 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
74 #define USERDATA_DIR_MODE (MODE_R_ALL | MODE_X_ALL | S_IWUSR)
75 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
76 #define SCORE_PERMS LEVEL_PERMS
77 #define TAPE_PERMS LEVEL_PERMS
78 #define SETUP_PERMS LEVEL_PERMS
80 /* sort priorities of level series (also used as level series classes) */
81 #define LEVELCLASS_TUTORIAL_START 10
82 #define LEVELCLASS_TUTORIAL_END 99
83 #define LEVELCLASS_CLASSICS_START 100
84 #define LEVELCLASS_CLASSICS_END 199
85 #define LEVELCLASS_CONTRIBUTION_START 200
86 #define LEVELCLASS_CONTRIBUTION_END 299
87 #define LEVELCLASS_USER_START 300
88 #define LEVELCLASS_USER_END 399
89 #define LEVELCLASS_BD_START 400
90 #define LEVELCLASS_BD_END 499
91 #define LEVELCLASS_EM_START 500
92 #define LEVELCLASS_EM_END 599
93 #define LEVELCLASS_SP_START 600
94 #define LEVELCLASS_SP_END 699
95 #define LEVELCLASS_DX_START 700
96 #define LEVELCLASS_DX_END 799
98 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
99 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
100 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
101 #define LEVELCLASS_USER LEVELCLASS_USER_START
102 #define LEVELCLASS_BD LEVELCLASS_BD_START
103 #define LEVELCLASS_EM LEVELCLASS_EM_START
104 #define LEVELCLASS_SP LEVELCLASS_SP_START
105 #define LEVELCLASS_DX LEVELCLASS_DX_START
107 #define LEVELCLASS_UNDEFINED 999
109 #define NUM_LEVELCLASS_DESC 8
110 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
122 #define IS_LEVELCLASS_TUTORIAL(p) \
123 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
124 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
125 #define IS_LEVELCLASS_CLASSICS(p) \
126 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
127 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
128 #define IS_LEVELCLASS_CONTRIBUTION(p) \
129 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
130 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
131 #define IS_LEVELCLASS_USER(p) \
132 ((p)->sort_priority >= LEVELCLASS_USER_START && \
133 (p)->sort_priority <= LEVELCLASS_USER_END)
134 #define IS_LEVELCLASS_BD(p) \
135 ((p)->sort_priority >= LEVELCLASS_BD_START && \
136 (p)->sort_priority <= LEVELCLASS_BD_END)
137 #define IS_LEVELCLASS_EM(p) \
138 ((p)->sort_priority >= LEVELCLASS_EM_START && \
139 (p)->sort_priority <= LEVELCLASS_EM_END)
140 #define IS_LEVELCLASS_SP(p) \
141 ((p)->sort_priority >= LEVELCLASS_SP_START && \
142 (p)->sort_priority <= LEVELCLASS_SP_END)
143 #define IS_LEVELCLASS_DX(p) \
144 ((p)->sort_priority >= LEVELCLASS_DX_START && \
145 (p)->sort_priority <= LEVELCLASS_DX_END)
147 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
148 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
149 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
150 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
151 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
152 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
153 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
154 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
155 LEVELCLASS_UNDEFINED)
157 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
158 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
159 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
160 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
161 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
162 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
163 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
164 IS_LEVELCLASS_USER(n) ? FC_RED : \
167 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
168 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
169 IS_LEVELCLASS_BD(n) ? 2 : \
170 IS_LEVELCLASS_EM(n) ? 3 : \
171 IS_LEVELCLASS_SP(n) ? 4 : \
172 IS_LEVELCLASS_DX(n) ? 5 : \
173 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
174 IS_LEVELCLASS_USER(n) ? 7 : \
177 char *getLevelClassDescription(struct LevelDirInfo *ldi)
179 int position = ldi->sort_priority / 100;
181 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
182 return levelclass_desc[position];
184 return "Unknown Level Class";
187 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
188 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
190 char *getUserDataDir()
192 static char *userdata_dir = NULL;
196 char *home_dir = getHomeDir();
197 char *data_dir = USERDATA_DIRECTORY;
199 userdata_dir = getPath2(home_dir, data_dir);
205 static char *getSetupDir()
207 return getUserDataDir();
210 static char *getUserLevelDir(char *level_subdir)
212 static char *userlevel_dir = NULL;
213 char *data_dir = getUserDataDir();
214 char *userlevel_subdir = LEVELS_DIRECTORY;
219 if (strlen(level_subdir) > 0)
220 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
222 userlevel_dir = getPath2(data_dir, userlevel_subdir);
224 return userlevel_dir;
227 static char *getTapeDir(char *level_subdir)
229 static char *tape_dir = NULL;
230 char *data_dir = getUserDataDir();
231 char *tape_subdir = TAPES_DIRECTORY;
236 if (strlen(level_subdir) > 0)
237 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
239 tape_dir = getPath2(data_dir, tape_subdir);
244 static char *getScoreDir(char *level_subdir)
246 static char *score_dir = NULL;
247 char *data_dir = options.rw_base_directory;
248 char *score_subdir = SCORES_DIRECTORY;
253 if (strlen(level_subdir) > 0)
254 score_dir = getPath3(data_dir, score_subdir, level_subdir);
256 score_dir = getPath2(data_dir, score_subdir);
261 static char *getLevelSetupDir(char *level_subdir)
263 static char *levelsetup_dir = NULL;
264 char *data_dir = getUserDataDir();
265 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
268 free(levelsetup_dir);
270 if (strlen(level_subdir) > 0)
271 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
273 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
275 return levelsetup_dir;
278 static char *getLevelFilename(int nr)
280 static char *filename = NULL;
281 char basename[MAX_FILENAME_LEN];
283 if (filename != NULL)
286 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
287 filename = getPath3((leveldir_current->user_defined ?
288 getUserLevelDir("") :
289 options.level_directory),
290 leveldir_current->fullpath,
296 static char *getTapeFilename(int nr)
298 static char *filename = NULL;
299 char basename[MAX_FILENAME_LEN];
301 if (filename != NULL)
304 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
305 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
310 static char *getScoreFilename(int nr)
312 static char *filename = NULL;
313 char basename[MAX_FILENAME_LEN];
315 if (filename != NULL)
318 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
319 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
324 static void createDirectory(char *dir, char *text)
326 if (access(dir, F_OK) != 0)
327 if (mkdir(dir, USERDATA_DIR_MODE) != 0)
328 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
331 static void InitUserDataDirectory()
333 createDirectory(getUserDataDir(), "user data");
336 static void InitTapeDirectory(char *level_subdir)
338 createDirectory(getUserDataDir(), "user data");
339 createDirectory(getTapeDir(""), "main tape");
340 createDirectory(getTapeDir(level_subdir), "level tape");
343 static void InitScoreDirectory(char *level_subdir)
345 createDirectory(getScoreDir(""), "main score");
346 createDirectory(getScoreDir(level_subdir), "level score");
349 static void InitUserLevelDirectory(char *level_subdir)
351 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
353 createDirectory(getUserDataDir(), "user data");
354 createDirectory(getUserLevelDir(""), "main user level");
355 createDirectory(getUserLevelDir(level_subdir), "user level");
361 static void InitLevelSetupDirectory(char *level_subdir)
363 createDirectory(getUserDataDir(), "user data");
364 createDirectory(getLevelSetupDir(""), "main level setup");
365 createDirectory(getLevelSetupDir(level_subdir), "level setup");
368 static void setLevelInfoToDefaults()
372 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
373 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
375 for(x=0; x<MAX_LEV_FIELDX; x++)
376 for(y=0; y<MAX_LEV_FIELDY; y++)
377 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
380 level.gems_needed = 0;
381 level.amoeba_speed = 10;
382 level.time_magic_wall = 10;
383 level.time_wheel = 10;
384 level.time_light = 10;
385 level.time_timegate = 10;
386 level.amoeba_content = EL_DIAMANT;
387 level.double_speed = FALSE;
388 level.gravity = FALSE;
390 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
391 level.name[i] = '\0';
392 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
393 level.author[i] = '\0';
395 strcpy(level.name, NAMELESS_LEVEL_NAME);
396 strcpy(level.author, ANONYMOUS_NAME);
398 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
401 level.num_yam_contents = STD_ELEMENT_CONTENTS;
402 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
405 level.yam_content[i][x][y] = EL_FELSBROCKEN;
407 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
408 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
409 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
411 BorderElement = EL_BETON;
413 /* try to determine better author name than 'anonymous' */
414 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
416 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
417 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
421 switch (LEVELCLASS(leveldir_current))
423 case LEVELCLASS_TUTORIAL:
424 strcpy(level.author, PROGRAM_AUTHOR_STRING);
427 case LEVELCLASS_CONTRIBUTION:
428 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
429 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
432 case LEVELCLASS_USER:
433 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
434 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
438 /* keep default value */
444 void LoadLevel(int level_nr)
447 char *filename = getLevelFilename(level_nr);
448 char cookie[MAX_LINE_LEN];
449 char chunk[CHUNK_ID_LEN + 1];
450 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
451 int file_version = FILE_VERSION_1_4; /* last version of level files */
455 /* always start with reliable default values */
456 setLevelInfoToDefaults();
458 if (!(file = fopen(filename, "r")))
460 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
464 /* check file identifier */
465 fgets(cookie, MAX_LINE_LEN, file);
466 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
467 cookie[strlen(cookie) - 1] = '\0';
469 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
470 file_version = FILE_VERSION_1_0;
471 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
472 file_version = FILE_VERSION_1_2;
473 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
475 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
480 /* read chunk "HEAD" */
481 if (file_version >= FILE_VERSION_1_2)
483 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
484 if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
486 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
492 lev_fieldx = level.fieldx = fgetc(file);
493 lev_fieldy = level.fieldy = fgetc(file);
495 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
496 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
498 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
499 level.name[i] = fgetc(file);
500 level.name[MAX_LEVEL_NAME_LEN] = 0;
502 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
503 level.score[i] = fgetc(file);
505 level.num_yam_contents = STD_ELEMENT_CONTENTS;
506 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
512 if (i < STD_ELEMENT_CONTENTS)
513 level.yam_content[i][x][y] = fgetc(file);
515 level.yam_content[i][x][y] = EL_LEERRAUM;
520 level.amoeba_speed = fgetc(file);
521 level.time_magic_wall = fgetc(file);
522 level.time_wheel = fgetc(file);
523 level.amoeba_content = fgetc(file);
524 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
525 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
527 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
529 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
532 if (file_version >= FILE_VERSION_1_2)
534 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
536 /* look for optional author chunk */
537 if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
539 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
540 level.author[i] = fgetc(file);
541 level.author[MAX_LEVEL_NAME_LEN] = 0;
543 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
546 /* look for optional content chunk */
547 if (strcmp(chunk, "CONT") == 0 &&
548 chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
551 level.num_yam_contents = fgetc(file);
555 if (level.num_yam_contents < 1 ||
556 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
559 printf("WARNING: num_yam_contents == %d (corrected)\n",
560 level.num_yam_contents);
562 level.num_yam_contents = STD_ELEMENT_CONTENTS;
565 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
568 level.yam_content[i][x][y] =
570 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
573 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
576 /* next check body chunk identifier and chunk length */
577 if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
579 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
585 /* clear all other level fields (needed if resized in level editor later) */
586 for(x=0; x<MAX_LEV_FIELDX; x++)
587 for(y=0; y<MAX_LEV_FIELDY; y++)
588 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
590 /* now read in the valid level fields from level file */
591 for(y=0; y<lev_fieldy; y++)
592 for(x=0; x<lev_fieldx; x++)
593 Feld[x][y] = Ur[x][y] =
595 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
600 /* player was faster than monsters in pre-1.0 levels */
601 if (file_version == FILE_VERSION_1_0 &&
602 IS_LEVELCLASS_CONTRIBUTION(leveldir_current))
604 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
605 Error(ERR_WARN, "using high speed movement for player");
606 level.double_speed = TRUE;
609 /* determine border element for this level */
613 void SaveLevel(int level_nr)
616 char *filename = getLevelFilename(level_nr);
617 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
618 char *oldest_possible_cookie;
621 if (!(file = fopen(filename, "w")))
623 Error(ERR_WARN, "cannot save level file '%s'", filename);
627 /* check yam content for 16-bit elements */
628 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
631 if (level.yam_content[i][x][y] > 255)
632 encoding_16bit = TRUE;
634 /* check level field for 16-bit elements */
635 for(y=0; y<lev_fieldy; y++)
636 for(x=0; x<lev_fieldx; x++)
638 encoding_16bit = TRUE;
640 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
642 fputs(oldest_possible_cookie, file); /* file identifier */
645 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
647 fputc(level.fieldx, file);
648 fputc(level.fieldy, file);
650 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
651 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
653 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
654 fputc(level.name[i], file);
655 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
656 fputc(level.score[i], file);
657 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
660 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
661 fputc(level.amoeba_speed, file);
662 fputc(level.time_magic_wall, file);
663 fputc(level.time_wheel, file);
664 fputc(level.amoeba_content, file);
665 fputc((level.double_speed ? 1 : 0), file);
666 fputc((level.gravity ? 1 : 0), file);
668 fputc((encoding_16bit ? 1 : 0), file);
670 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
673 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
675 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
676 fputc(level.author[i], file);
678 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
679 BYTE_ORDER_BIG_ENDIAN);
681 fputc(EL_MAMPFER, file);
682 fputc(level.num_yam_contents, file);
686 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
690 putFile16BitInteger(file, level.yam_content[i][x][y],
691 BYTE_ORDER_BIG_ENDIAN);
693 fputc(level.yam_content[i][x][y], file);
695 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
697 for(y=0; y<lev_fieldy; y++)
698 for(x=0; x<lev_fieldx; x++)
700 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
702 fputc(Ur[x][y], file);
706 chmod(filename, LEVEL_PERMS);
709 void LoadTape(int level_nr)
712 char *filename = getTapeFilename(level_nr);
713 char cookie[MAX_LINE_LEN];
714 char chunk[CHUNK_ID_LEN + 1];
716 int num_participating_players;
717 int file_version = FILE_VERSION_1_2; /* last version of tape files */
720 /* always start with reliable default values (empty tape) */
723 /* default values (also for pre-1.2 tapes) with only the first player */
724 tape.player_participates[0] = TRUE;
725 for(i=1; i<MAX_PLAYERS; i++)
726 tape.player_participates[i] = FALSE;
728 /* at least one (default: the first) player participates in every tape */
729 num_participating_players = 1;
731 if (!(file = fopen(filename, "r")))
734 /* check file identifier */
735 fgets(cookie, MAX_LINE_LEN, file);
736 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
737 cookie[strlen(cookie) - 1] = '\0';
739 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
740 file_version = FILE_VERSION_1_0;
741 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
743 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
748 /* read chunk "HEAD" */
749 if (file_version >= FILE_VERSION_1_2)
751 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
752 if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
754 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
760 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
761 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
762 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
764 /* read header fields that are new since version 1.2 */
765 if (file_version >= FILE_VERSION_1_2)
767 byte store_participating_players = fgetc(file);
769 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
772 /* since version 1.2, tapes store which players participate in the tape */
773 num_participating_players = 0;
774 for(i=0; i<MAX_PLAYERS; i++)
776 tape.player_participates[i] = FALSE;
778 if (store_participating_players & (1 << i))
780 tape.player_participates[i] = TRUE;
781 num_participating_players++;
786 tape.level_nr = level_nr;
788 tape.changed = FALSE;
790 tape.recording = FALSE;
791 tape.playing = FALSE;
792 tape.pausing = FALSE;
794 /* read chunk "BODY" */
795 if (file_version >= FILE_VERSION_1_2)
797 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
798 if (strcmp(chunk, "BODY") ||
799 chunk_length != (num_participating_players + 1) * tape.length)
801 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
807 for(i=0; i<tape.length; i++)
809 if (i >= MAX_TAPELEN)
812 for(j=0; j<MAX_PLAYERS; j++)
814 tape.pos[i].action[j] = MV_NO_MOVING;
816 if (tape.player_participates[j])
817 tape.pos[i].action[j] = fgetc(file);
820 tape.pos[i].delay = fgetc(file);
822 if (file_version == FILE_VERSION_1_0)
824 /* eliminate possible diagonal moves in old tapes */
825 /* this is only for backward compatibility */
827 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
828 byte action = tape.pos[i].action[0];
829 int k, num_moves = 0;
833 if (action & joy_dir[k])
835 tape.pos[i + num_moves].action[0] = joy_dir[k];
837 tape.pos[i + num_moves].delay = 0;
846 tape.length += num_moves;
856 if (i != tape.length)
857 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
859 tape.length_seconds = GetTapeLength();
862 void SaveTape(int level_nr)
865 char *filename = getTapeFilename(level_nr);
867 boolean new_tape = TRUE;
868 byte store_participating_players;
869 int num_participating_players;
871 InitTapeDirectory(leveldir_current->filename);
873 /* if a tape still exists, ask to overwrite it */
874 if (access(filename, F_OK) == 0)
877 if (!Request("Replace old tape ?", REQ_ASK))
881 /* count number of players and set corresponding bits for compact storage */
882 store_participating_players = 0;
883 num_participating_players = 0;
884 for(i=0; i<MAX_PLAYERS; i++)
886 if (tape.player_participates[i])
888 num_participating_players++;
889 store_participating_players |= (1 << i);
893 if (!(file = fopen(filename, "w")))
895 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
899 fputs(TAPE_COOKIE, file); /* file identifier */
902 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
904 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
905 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
906 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
908 fputc(store_participating_players, file);
910 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
913 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
914 BYTE_ORDER_BIG_ENDIAN);
916 for(i=0; i<tape.length; i++)
920 for(j=0; j<MAX_PLAYERS; j++)
921 if (tape.player_participates[j])
922 fputc(tape.pos[i].action[j], file);
924 fputc(tape.pos[i].delay, file);
929 chmod(filename, TAPE_PERMS);
931 tape.changed = FALSE;
934 Request("tape saved !", REQ_CONFIRM);
937 void LoadScore(int level_nr)
940 char *filename = getScoreFilename(level_nr);
941 char cookie[MAX_LINE_LEN];
942 char line[MAX_LINE_LEN];
946 /* always start with reliable default values */
947 for(i=0; i<MAX_SCORE_ENTRIES; i++)
949 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
950 highscore[i].Score = 0;
953 if (!(file = fopen(filename, "r")))
956 /* check file identifier */
957 fgets(cookie, MAX_LINE_LEN, file);
958 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
959 cookie[strlen(cookie) - 1] = '\0';
961 if (strcmp(cookie, SCORE_COOKIE) != 0)
963 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
968 for(i=0; i<MAX_SCORE_ENTRIES; i++)
970 fscanf(file, "%d", &highscore[i].Score);
971 fgets(line, MAX_LINE_LEN, file);
973 if (line[strlen(line) - 1] == '\n')
974 line[strlen(line) - 1] = '\0';
976 for (line_ptr = line; *line_ptr; line_ptr++)
978 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
980 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
981 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
990 void SaveScore(int level_nr)
993 char *filename = getScoreFilename(level_nr);
996 InitScoreDirectory(leveldir_current->filename);
998 if (!(file = fopen(filename, "w")))
1000 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1004 fprintf(file, "%s\n\n", SCORE_COOKIE);
1006 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1007 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1011 chmod(filename, SCORE_PERMS);
1014 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1015 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1016 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1017 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1018 #define TOKEN_STR_PLAYER_PREFIX "player_"
1020 #define TOKEN_VALUE_POSITION 30
1023 #define SETUP_TOKEN_PLAYER_NAME 0
1024 #define SETUP_TOKEN_SOUND 1
1025 #define SETUP_TOKEN_SOUND_LOOPS 2
1026 #define SETUP_TOKEN_SOUND_MUSIC 3
1027 #define SETUP_TOKEN_SOUND_SIMPLE 4
1030 #define SETUP_TOKEN_TOONS 5
1031 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1034 #define SETUP_TOKEN_SCROLL_DELAY 5
1035 #define SETUP_TOKEN_SOFT_SCROLLING 6
1036 #define SETUP_TOKEN_FADING 7
1037 #define SETUP_TOKEN_AUTORECORD 8
1038 #define SETUP_TOKEN_QUICK_DOORS 9
1039 #define SETUP_TOKEN_TEAM_MODE 10
1040 #define SETUP_TOKEN_HANDICAP 11
1041 #define SETUP_TOKEN_TIME_LIMIT 12
1044 #define SETUP_TOKEN_USE_JOYSTICK 13
1045 #define SETUP_TOKEN_JOY_DEVICE_NAME 14
1046 #define SETUP_TOKEN_JOY_XLEFT 15
1047 #define SETUP_TOKEN_JOY_XMIDDLE 16
1048 #define SETUP_TOKEN_JOY_XRIGHT 17
1049 #define SETUP_TOKEN_JOY_YUPPER 18
1050 #define SETUP_TOKEN_JOY_YMIDDLE 19
1051 #define SETUP_TOKEN_JOY_YLOWER 20
1052 #define SETUP_TOKEN_JOY_SNAP 21
1053 #define SETUP_TOKEN_JOY_BOMB 22
1054 #define SETUP_TOKEN_KEY_LEFT 23
1055 #define SETUP_TOKEN_KEY_RIGHT 24
1056 #define SETUP_TOKEN_KEY_UP 25
1057 #define SETUP_TOKEN_KEY_DOWN 26
1058 #define SETUP_TOKEN_KEY_SNAP 27
1059 #define SETUP_TOKEN_KEY_BOMB 28
1061 /* level directory info */
1062 #define LEVELINFO_TOKEN_NAME 29
1063 #define LEVELINFO_TOKEN_NAME_SHORT 30
1064 #define LEVELINFO_TOKEN_NAME_SORTING 31
1065 #define LEVELINFO_TOKEN_AUTHOR 32
1066 #define LEVELINFO_TOKEN_IMPORTED_FROM 33
1067 #define LEVELINFO_TOKEN_LEVELS 34
1068 #define LEVELINFO_TOKEN_FIRST_LEVEL 35
1069 #define LEVELINFO_TOKEN_SORT_PRIORITY 36
1070 #define LEVELINFO_TOKEN_LEVEL_GROUP 37
1071 #define LEVELINFO_TOKEN_READONLY 38
1073 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1074 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_TIME_LIMIT
1076 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1077 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1079 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1080 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1082 #define TYPE_BOOLEAN 1
1083 #define TYPE_SWITCH 2
1084 #define TYPE_KEYSYM 3
1085 #define TYPE_INTEGER 4
1086 #define TYPE_STRING 5
1088 static struct SetupInfo si;
1089 static struct SetupInputInfo sii;
1090 static struct LevelDirInfo ldi;
1099 { TYPE_STRING, &si.player_name, "player_name" },
1100 { TYPE_SWITCH, &si.sound, "sound" },
1101 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1102 { TYPE_SWITCH, &si.sound_music, "background_music" },
1103 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1106 { TYPE_SWITCH, &si.toons, "toons" },
1107 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1110 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1111 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1112 { TYPE_SWITCH, &si.fading, "screen_fading" },
1113 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1114 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1115 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1116 { TYPE_SWITCH, &si.handicap, "handicap" },
1117 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1120 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1121 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1122 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1123 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1124 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1125 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1126 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1127 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1128 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1129 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1130 { TYPE_KEYSYM, &sii.key.left, ".key.move_left" },
1131 { TYPE_KEYSYM, &sii.key.right, ".key.move_right" },
1132 { TYPE_KEYSYM, &sii.key.up, ".key.move_up" },
1133 { TYPE_KEYSYM, &sii.key.down, ".key.move_down" },
1134 { TYPE_KEYSYM, &sii.key.snap, ".key.snap_field" },
1135 { TYPE_KEYSYM, &sii.key.bomb, ".key.place_bomb" },
1137 /* level directory info */
1138 { TYPE_STRING, &ldi.name, "name" },
1139 { TYPE_STRING, &ldi.name_short, "name_short" },
1140 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1141 { TYPE_STRING, &ldi.author, "author" },
1142 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1143 { TYPE_INTEGER, &ldi.levels, "levels" },
1144 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1145 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1146 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1147 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1150 static char *string_tolower(char *s)
1152 static char s_lower[100];
1155 if (strlen(s) >= 100)
1160 for (i=0; i<strlen(s_lower); i++)
1161 s_lower[i] = tolower(s_lower[i]);
1166 static int get_string_integer_value(char *s)
1168 static char *number_text[][3] =
1170 { "0", "zero", "null", },
1171 { "1", "one", "first" },
1172 { "2", "two", "second" },
1173 { "3", "three", "third" },
1174 { "4", "four", "fourth" },
1175 { "5", "five", "fifth" },
1176 { "6", "six", "sixth" },
1177 { "7", "seven", "seventh" },
1178 { "8", "eight", "eighth" },
1179 { "9", "nine", "ninth" },
1180 { "10", "ten", "tenth" },
1181 { "11", "eleven", "eleventh" },
1182 { "12", "twelve", "twelfth" },
1187 for (i=0; i<13; i++)
1189 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1195 static boolean get_string_boolean_value(char *s)
1197 if (strcmp(string_tolower(s), "true") == 0 ||
1198 strcmp(string_tolower(s), "yes") == 0 ||
1199 strcmp(string_tolower(s), "on") == 0 ||
1200 get_string_integer_value(s) == 1)
1206 static char *getFormattedSetupEntry(char *token, char *value)
1209 static char entry[MAX_LINE_LEN];
1211 sprintf(entry, "%s:", token);
1212 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1216 strcat(entry, value);
1221 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1223 if (!setup_file_list)
1226 if (setup_file_list->token)
1227 free(setup_file_list->token);
1228 if (setup_file_list->value)
1229 free(setup_file_list->value);
1230 if (setup_file_list->next)
1231 freeSetupFileList(setup_file_list->next);
1232 free(setup_file_list);
1235 static struct SetupFileList *newSetupFileList(char *token, char *value)
1237 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1239 new->token = checked_malloc(strlen(token) + 1);
1240 strcpy(new->token, token);
1242 new->value = checked_malloc(strlen(value) + 1);
1243 strcpy(new->value, value);
1250 static char *getTokenValue(struct SetupFileList *setup_file_list,
1253 if (!setup_file_list)
1256 if (strcmp(setup_file_list->token, token) == 0)
1257 return setup_file_list->value;
1259 return getTokenValue(setup_file_list->next, token);
1262 static void setTokenValue(struct SetupFileList *setup_file_list,
1263 char *token, char *value)
1265 if (!setup_file_list)
1268 if (strcmp(setup_file_list->token, token) == 0)
1270 free(setup_file_list->value);
1271 setup_file_list->value = checked_malloc(strlen(value) + 1);
1272 strcpy(setup_file_list->value, value);
1274 else if (setup_file_list->next == NULL)
1275 setup_file_list->next = newSetupFileList(token, value);
1277 setTokenValue(setup_file_list->next, token, value);
1281 static void printSetupFileList(struct SetupFileList *setup_file_list)
1283 if (!setup_file_list)
1286 printf("token: '%s'\n", setup_file_list->token);
1287 printf("value: '%s'\n", setup_file_list->value);
1289 printSetupFileList(setup_file_list->next);
1293 static struct SetupFileList *loadSetupFileList(char *filename)
1296 char line[MAX_LINE_LEN];
1297 char *token, *value, *line_ptr;
1298 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1299 struct SetupFileList *first_valid_list_entry;
1303 if (!(file = fopen(filename, "r")))
1305 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1311 /* read next line of input file */
1312 if (!fgets(line, MAX_LINE_LEN, file))
1315 /* cut trailing comment or whitespace from input line */
1316 for (line_ptr = line; *line_ptr; line_ptr++)
1318 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1325 /* cut trailing whitespaces from input line */
1326 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1327 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1330 /* ignore empty lines */
1334 line_len = strlen(line);
1336 /* cut leading whitespaces from token */
1337 for (token = line; *token; token++)
1338 if (*token != ' ' && *token != '\t')
1341 /* find end of token */
1342 for (line_ptr = token; *line_ptr; line_ptr++)
1344 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1351 if (line_ptr < line + line_len)
1352 value = line_ptr + 1;
1356 /* cut leading whitespaces from value */
1357 for (; *value; value++)
1358 if (*value != ' ' && *value != '\t')
1361 if (*token && *value)
1362 setTokenValue(setup_file_list, token, value);
1367 first_valid_list_entry = setup_file_list->next;
1369 /* free empty list header */
1370 setup_file_list->next = NULL;
1371 freeSetupFileList(setup_file_list);
1373 if (first_valid_list_entry == NULL)
1374 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1376 return first_valid_list_entry;
1379 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1382 if (!setup_file_list)
1385 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1387 if (strcmp(setup_file_list->value, identifier) != 0)
1389 Error(ERR_WARN, "configuration file has wrong version");
1396 if (setup_file_list->next)
1397 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1400 Error(ERR_WARN, "configuration file has no version information");
1405 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1407 ldi->filename = NULL;
1408 ldi->fullpath = NULL;
1409 ldi->basepath = NULL;
1410 ldi->name = getStringCopy(ANONYMOUS_NAME);
1411 ldi->name_short = NULL;
1412 ldi->name_sorting = NULL;
1413 ldi->author = getStringCopy(ANONYMOUS_NAME);
1414 ldi->imported_from = NULL;
1416 ldi->first_level = 0;
1417 ldi->last_level = 0;
1418 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1419 ldi->level_group = FALSE;
1420 ldi->parent_link = FALSE;
1421 ldi->user_defined = FALSE;
1422 ldi->readonly = TRUE;
1424 ldi->class_desc = NULL;
1425 ldi->handicap_level = 0;
1427 ldi->cl_cursor = -1;
1429 ldi->node_parent = NULL;
1430 ldi->node_group = NULL;
1434 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1435 struct LevelDirInfo *parent)
1439 setLevelDirInfoToDefaults(ldi);
1443 /* first copy all values from the parent structure ... */
1446 /* ... then set all fields to default that cannot be inherited from parent.
1447 This is especially important for all those fields that can be set from
1448 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1449 calls 'free()' for all already set token values which requires that no
1450 other structure's pointer may point to them!
1453 ldi->filename = NULL;
1454 ldi->fullpath = NULL;
1455 ldi->basepath = NULL;
1456 ldi->name = getStringCopy(ANONYMOUS_NAME);
1457 ldi->name_short = NULL;
1458 ldi->name_sorting = NULL;
1459 ldi->author = getStringCopy(parent->author);
1460 ldi->imported_from = getStringCopy(parent->imported_from);
1462 ldi->level_group = FALSE;
1463 ldi->parent_link = FALSE;
1465 ldi->node_parent = parent;
1466 ldi->node_group = NULL;
1470 static void setSetupInfoToDefaults(struct SetupInfo *si)
1474 si->player_name = getStringCopy(getLoginName());
1477 si->sound_loops = TRUE;
1478 si->sound_music = TRUE;
1479 si->sound_simple = TRUE;
1481 si->double_buffering = TRUE;
1482 si->direct_draw = !si->double_buffering;
1483 si->scroll_delay = TRUE;
1484 si->soft_scrolling = TRUE;
1486 si->autorecord = TRUE;
1487 si->quick_doors = FALSE;
1488 si->team_mode = FALSE;
1489 si->handicap = TRUE;
1490 si->time_limit = TRUE;
1492 for (i=0; i<MAX_PLAYERS; i++)
1494 si->input[i].use_joystick = FALSE;
1495 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1496 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1497 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1498 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1499 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1500 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1501 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1502 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1503 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1504 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KEY_UNDEFINDED);
1505 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
1506 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KEY_UNDEFINDED);
1507 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KEY_UNDEFINDED);
1508 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KEY_UNDEFINDED);
1509 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KEY_UNDEFINDED);
1513 static void setSetupInfo(int token_nr, char *token_value)
1515 int token_type = token_info[token_nr].type;
1516 void *setup_value = token_info[token_nr].value;
1518 if (token_value == NULL)
1521 /* set setup field to corresponding token value */
1526 *(boolean *)setup_value = get_string_boolean_value(token_value);
1530 *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
1534 *(int *)setup_value = get_string_integer_value(token_value);
1538 if (*(char **)setup_value != NULL)
1539 free(*(char **)setup_value);
1540 *(char **)setup_value = getStringCopy(token_value);
1548 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1552 if (!setup_file_list)
1555 /* handle global setup values */
1557 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1558 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1561 /* handle player specific setup values */
1562 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1566 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1568 sii = setup.input[pnr];
1569 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1571 char full_token[100];
1573 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1574 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1576 setup.input[pnr] = sii;
1580 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1582 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1583 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1586 if (entry1->parent_link || entry2->parent_link)
1587 compare_result = (entry1->parent_link ? -1 : +1);
1588 else if (entry1->sort_priority == entry2->sort_priority)
1590 char *name1 = getStringToLower(entry1->name_sorting);
1591 char *name2 = getStringToLower(entry2->name_sorting);
1593 compare_result = strcmp(name1, name2);
1598 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1599 compare_result = entry1->sort_priority - entry2->sort_priority;
1601 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1603 return compare_result;
1606 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1608 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1610 setLevelDirInfoToDefaults(leveldir_new);
1612 leveldir_new->node_parent = node_parent;
1613 leveldir_new->parent_link = TRUE;
1615 leveldir_new->name = ".. (parent directory)";
1616 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1617 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1619 leveldir_new->filename = "..";
1620 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1622 leveldir_new->sort_priority = node_parent->sort_priority;
1623 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1625 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1628 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1629 struct LevelDirInfo *node_parent,
1630 char *level_directory)
1633 struct dirent *dir_entry;
1634 boolean valid_entry_found = FALSE;
1636 if ((dir = opendir(level_directory)) == NULL)
1638 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1642 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1644 struct SetupFileList *setup_file_list = NULL;
1645 struct stat file_status;
1646 char *directory_name = dir_entry->d_name;
1647 char *directory_path = getPath2(level_directory, directory_name);
1648 char *filename = NULL;
1650 /* skip entries for current and parent directory */
1651 if (strcmp(directory_name, ".") == 0 ||
1652 strcmp(directory_name, "..") == 0)
1654 free(directory_path);
1658 /* find out if directory entry is itself a directory */
1659 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1660 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1662 free(directory_path);
1666 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1667 setup_file_list = loadSetupFileList(filename);
1669 if (setup_file_list)
1671 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1674 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1675 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1677 /* set all structure fields according to the token/value pairs */
1678 ldi = *leveldir_new;
1679 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1680 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1681 *leveldir_new = ldi;
1683 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1685 if (leveldir_new->name_short == NULL)
1686 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1688 if (leveldir_new->name_sorting == NULL)
1689 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1691 leveldir_new->filename = getStringCopy(directory_name);
1693 if (node_parent == NULL) /* top level group */
1695 leveldir_new->basepath = level_directory;
1696 leveldir_new->fullpath = leveldir_new->filename;
1698 else /* sub level group */
1700 leveldir_new->basepath = node_parent->basepath;
1701 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1705 if (leveldir_new->levels < 1)
1706 leveldir_new->levels = 1;
1708 leveldir_new->last_level =
1709 leveldir_new->first_level + leveldir_new->levels - 1;
1711 leveldir_new->user_defined =
1712 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1714 leveldir_new->color = LEVELCOLOR(leveldir_new);
1715 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1717 leveldir_new->handicap_level = /* set handicap to default value */
1718 (leveldir_new->user_defined ?
1719 leveldir_new->last_level :
1720 leveldir_new->first_level);
1722 pushLevelDirInfo(node_first, leveldir_new);
1724 freeSetupFileList(setup_file_list);
1725 valid_entry_found = TRUE;
1727 if (leveldir_new->level_group)
1729 /* create node to link back to current level directory */
1730 createParentLevelDirNode(leveldir_new);
1732 /* step into sub-directory and look for more level series */
1733 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1734 leveldir_new, directory_path);
1738 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1740 free(directory_path);
1746 if (!valid_entry_found)
1747 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1751 void LoadLevelInfo()
1753 InitUserLevelDirectory(getLoginName());
1755 DrawInitText("Loading level series:", 120, FC_GREEN);
1757 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1758 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1760 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1762 if (leveldir_first == NULL)
1763 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1765 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1768 dumpLevelDirInfo(leveldir_first, 0);
1772 static void SaveUserLevelInfo()
1778 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1780 if (!(file = fopen(filename, "w")))
1782 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1787 /* always start with reliable default values */
1788 setLevelDirInfoToDefaults(&ldi);
1790 ldi.name = getLoginName();
1791 ldi.author = getRealName();
1793 ldi.first_level = 1;
1794 ldi.sort_priority = LEVELCLASS_USER_START;
1795 ldi.readonly = FALSE;
1797 fprintf(file, "%s\n\n",
1798 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1800 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1801 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1802 i != LEVELINFO_TOKEN_NAME_SORTING &&
1803 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1804 fprintf(file, "%s\n", getSetupLine("", i));
1809 chmod(filename, SETUP_PERMS);
1815 struct SetupFileList *setup_file_list = NULL;
1817 /* always start with reliable default values */
1818 setSetupInfoToDefaults(&setup);
1820 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1822 setup_file_list = loadSetupFileList(filename);
1824 if (setup_file_list)
1826 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1827 decodeSetupFileList(setup_file_list);
1829 setup.direct_draw = !setup.double_buffering;
1831 freeSetupFileList(setup_file_list);
1833 /* needed to work around problems with fixed length strings */
1834 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1835 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1836 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1838 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1840 strcpy(new_name, setup.player_name);
1841 free(setup.player_name);
1842 setup.player_name = new_name;
1846 Error(ERR_WARN, "using default setup values");
1851 static char *getSetupLine(char *prefix, int token_nr)
1854 static char entry[MAX_LINE_LEN];
1855 int token_type = token_info[token_nr].type;
1856 void *setup_value = token_info[token_nr].value;
1857 char *token_text = token_info[token_nr].text;
1859 /* start with the prefix, token and some spaces to format output line */
1860 sprintf(entry, "%s%s:", prefix, token_text);
1861 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1864 /* continue with the token's value (which can have different types) */
1868 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1872 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1877 KeySym keysym = *(KeySym *)setup_value;
1878 char *keyname = getKeyNameFromKeySym(keysym);
1880 strcat(entry, getX11KeyNameFromKeySym(keysym));
1881 for (i=strlen(entry); i<50; i++)
1884 /* add comment, if useful */
1885 if (strcmp(keyname, "(undefined)") != 0 &&
1886 strcmp(keyname, "(unknown)") != 0)
1888 strcat(entry, "# ");
1889 strcat(entry, keyname);
1896 char buffer[MAX_LINE_LEN];
1898 sprintf(buffer, "%d", *(int *)setup_value);
1899 strcat(entry, buffer);
1904 strcat(entry, *(char **)setup_value);
1920 InitUserDataDirectory();
1922 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1924 if (!(file = fopen(filename, "w")))
1926 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1931 fprintf(file, "%s\n",
1932 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1933 fprintf(file, "\n");
1935 /* handle global setup values */
1937 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1939 fprintf(file, "%s\n", getSetupLine("", i));
1941 /* just to make things nicer :) */
1942 if (i == SETUP_TOKEN_PLAYER_NAME)
1943 fprintf(file, "\n");
1946 /* handle player specific setup values */
1947 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1951 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1952 fprintf(file, "\n");
1954 sii = setup.input[pnr];
1955 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1956 fprintf(file, "%s\n", getSetupLine(prefix, i));
1962 chmod(filename, SETUP_PERMS);
1965 void LoadLevelSetup_LastSeries()
1968 struct SetupFileList *level_setup_list = NULL;
1970 /* always start with reliable default values */
1971 leveldir_current = leveldir_first;
1973 /* ----------------------------------------------------------------------- */
1974 /* ~/.rocksndiamonds/levelsetup.conf */
1975 /* ----------------------------------------------------------------------- */
1977 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1979 if ((level_setup_list = loadSetupFileList(filename)))
1981 char *last_level_series =
1982 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1984 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
1985 if (leveldir_current == NULL)
1986 leveldir_current = leveldir_first;
1988 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1990 freeSetupFileList(level_setup_list);
1993 Error(ERR_WARN, "using default setup values");
1998 void SaveLevelSetup_LastSeries()
2001 char *level_subdir = leveldir_current->filename;
2004 /* ----------------------------------------------------------------------- */
2005 /* ~/.rocksndiamonds/levelsetup.conf */
2006 /* ----------------------------------------------------------------------- */
2008 InitUserDataDirectory();
2010 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2012 if (!(file = fopen(filename, "w")))
2014 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2019 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2020 LEVELSETUP_COOKIE));
2021 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2027 chmod(filename, SETUP_PERMS);
2030 static void checkSeriesInfo()
2032 static char *level_directory = NULL;
2034 struct dirent *dir_entry;
2036 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2038 level_directory = getPath2((leveldir_current->user_defined ?
2039 getUserLevelDir("") :
2040 options.level_directory),
2041 leveldir_current->filename);
2043 if ((dir = opendir(level_directory)) == NULL)
2045 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2049 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2051 if (strlen(dir_entry->d_name) > 4 &&
2052 dir_entry->d_name[3] == '.' &&
2053 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2055 char levelnum_str[4];
2058 strncpy(levelnum_str, dir_entry->d_name, 3);
2059 levelnum_str[3] = '\0';
2061 levelnum_value = atoi(levelnum_str);
2063 if (levelnum_value < leveldir_current->first_level)
2065 Error(ERR_WARN, "additional level %d found", levelnum_value);
2066 leveldir_current->first_level = levelnum_value;
2068 else if (levelnum_value > leveldir_current->last_level)
2070 Error(ERR_WARN, "additional level %d found", levelnum_value);
2071 leveldir_current->last_level = levelnum_value;
2079 void LoadLevelSetup_SeriesInfo()
2082 struct SetupFileList *level_setup_list = NULL;
2083 char *level_subdir = leveldir_current->filename;
2085 /* always start with reliable default values */
2086 level_nr = leveldir_current->first_level;
2088 checkSeriesInfo(leveldir_current);
2090 /* ----------------------------------------------------------------------- */
2091 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2092 /* ----------------------------------------------------------------------- */
2094 level_subdir = leveldir_current->filename;
2096 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2098 if ((level_setup_list = loadSetupFileList(filename)))
2102 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2106 level_nr = atoi(token_value);
2108 if (level_nr < leveldir_current->first_level)
2109 level_nr = leveldir_current->first_level;
2110 if (level_nr > leveldir_current->last_level)
2111 level_nr = leveldir_current->last_level;
2114 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2118 int level_nr = atoi(token_value);
2120 if (level_nr < leveldir_current->first_level)
2121 level_nr = leveldir_current->first_level;
2122 if (level_nr > leveldir_current->last_level + 1)
2123 level_nr = leveldir_current->last_level;
2125 if (leveldir_current->user_defined)
2126 level_nr = leveldir_current->last_level;
2128 leveldir_current->handicap_level = level_nr;
2131 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2133 freeSetupFileList(level_setup_list);
2136 Error(ERR_WARN, "using default setup values");
2141 void SaveLevelSetup_SeriesInfo()
2144 char *level_subdir = leveldir_current->filename;
2145 char *level_nr_str = int2str(level_nr, 0);
2146 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2149 /* ----------------------------------------------------------------------- */
2150 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2151 /* ----------------------------------------------------------------------- */
2153 InitLevelSetupDirectory(level_subdir);
2155 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2157 if (!(file = fopen(filename, "w")))
2159 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2164 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2165 LEVELSETUP_COOKIE));
2166 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2168 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2169 handicap_level_str));
2174 chmod(filename, SETUP_PERMS);
2178 void initErrorFile()
2182 InitUserDataDirectory();
2184 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2189 FILE *openErrorFile()
2194 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2195 error_file = fopen(filename, "a");
2201 void dumpErrorFile()
2206 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2207 error_file = fopen(filename, "r");
2210 if (error_file != NULL)
2212 while (!feof(error_file))
2213 fputc(fgetc(error_file), stderr);