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 static int checkLevelElement(int element)
446 if (element >= EL_FIRST_RUNTIME_EL)
448 Error(ERR_WARN, "invalid level element %d", element);
449 element = EL_CHAR_FRAGE;
455 void LoadLevel(int level_nr)
458 char *filename = getLevelFilename(level_nr);
459 char cookie[MAX_LINE_LEN];
460 char chunk[CHUNK_ID_LEN + 1];
461 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
462 int file_version = FILE_VERSION_1_4; /* last version of level files */
466 /* always start with reliable default values */
467 setLevelInfoToDefaults();
469 if (!(file = fopen(filename, "r")))
471 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
475 /* check file identifier */
476 fgets(cookie, MAX_LINE_LEN, file);
477 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
478 cookie[strlen(cookie) - 1] = '\0';
480 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
481 file_version = FILE_VERSION_1_0;
482 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
483 file_version = FILE_VERSION_1_2;
484 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
486 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
491 /* read chunk "HEAD" */
492 if (file_version >= FILE_VERSION_1_2)
494 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
495 if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
497 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
503 lev_fieldx = level.fieldx = fgetc(file);
504 lev_fieldy = level.fieldy = fgetc(file);
506 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
507 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
509 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
510 level.name[i] = fgetc(file);
511 level.name[MAX_LEVEL_NAME_LEN] = 0;
513 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
514 level.score[i] = fgetc(file);
516 level.num_yam_contents = STD_ELEMENT_CONTENTS;
517 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
523 if (i < STD_ELEMENT_CONTENTS)
524 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
526 level.yam_content[i][x][y] = EL_LEERRAUM;
531 level.amoeba_speed = fgetc(file);
532 level.time_magic_wall = fgetc(file);
533 level.time_wheel = fgetc(file);
534 level.amoeba_content = checkLevelElement(fgetc(file));
535 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
536 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
538 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
540 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
543 if (file_version >= FILE_VERSION_1_2)
545 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
547 /* look for optional author chunk */
548 if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
550 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
551 level.author[i] = fgetc(file);
552 level.author[MAX_LEVEL_NAME_LEN] = 0;
554 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
557 /* look for optional content chunk */
558 if (strcmp(chunk, "CONT") == 0 &&
559 chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
562 level.num_yam_contents = fgetc(file);
566 if (level.num_yam_contents < 1 ||
567 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
570 printf("WARNING: num_yam_contents == %d (corrected)\n",
571 level.num_yam_contents);
573 level.num_yam_contents = STD_ELEMENT_CONTENTS;
576 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
579 level.yam_content[i][x][y] =
580 checkLevelElement(encoding_16bit ?
581 getFile16BitInteger(file,
582 BYTE_ORDER_BIG_ENDIAN) :
585 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
588 /* next check body chunk identifier and chunk length */
589 if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
591 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
597 /* clear all other level fields (needed if resized in level editor later) */
598 for(x=0; x<MAX_LEV_FIELDX; x++)
599 for(y=0; y<MAX_LEV_FIELDY; y++)
600 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
602 /* now read in the valid level fields from level file */
603 for(y=0; y<lev_fieldy; y++)
604 for(x=0; x<lev_fieldx; x++)
605 Feld[x][y] = Ur[x][y] =
606 checkLevelElement(encoding_16bit ?
607 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
612 /* player was faster than monsters in pre-1.0 levels */
613 if (file_version == FILE_VERSION_1_0 &&
614 IS_LEVELCLASS_CONTRIBUTION(leveldir_current))
616 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
617 Error(ERR_WARN, "using high speed movement for player");
618 level.double_speed = TRUE;
621 /* determine border element for this level */
625 void SaveLevel(int level_nr)
628 char *filename = getLevelFilename(level_nr);
629 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
630 char *oldest_possible_cookie;
633 if (!(file = fopen(filename, "w")))
635 Error(ERR_WARN, "cannot save level file '%s'", filename);
639 /* check yam content for 16-bit elements */
640 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
643 if (level.yam_content[i][x][y] > 255)
644 encoding_16bit = TRUE;
646 /* check level field for 16-bit elements */
647 for(y=0; y<lev_fieldy; y++)
648 for(x=0; x<lev_fieldx; x++)
650 encoding_16bit = TRUE;
652 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
654 fputs(oldest_possible_cookie, file); /* file identifier */
657 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
659 fputc(level.fieldx, file);
660 fputc(level.fieldy, file);
662 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
663 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
665 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
666 fputc(level.name[i], file);
667 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
668 fputc(level.score[i], file);
669 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
672 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
673 fputc(level.amoeba_speed, file);
674 fputc(level.time_magic_wall, file);
675 fputc(level.time_wheel, file);
676 fputc(level.amoeba_content, file);
677 fputc((level.double_speed ? 1 : 0), file);
678 fputc((level.gravity ? 1 : 0), file);
680 fputc((encoding_16bit ? 1 : 0), file);
682 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
685 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
687 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
688 fputc(level.author[i], file);
690 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
691 BYTE_ORDER_BIG_ENDIAN);
693 fputc(EL_MAMPFER, file);
694 fputc(level.num_yam_contents, file);
698 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
702 putFile16BitInteger(file, level.yam_content[i][x][y],
703 BYTE_ORDER_BIG_ENDIAN);
705 fputc(level.yam_content[i][x][y], file);
707 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
709 for(y=0; y<lev_fieldy; y++)
710 for(x=0; x<lev_fieldx; x++)
712 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
714 fputc(Ur[x][y], file);
718 chmod(filename, LEVEL_PERMS);
721 void LoadTape(int level_nr)
724 char *filename = getTapeFilename(level_nr);
725 char cookie[MAX_LINE_LEN];
726 char chunk[CHUNK_ID_LEN + 1];
728 int num_participating_players;
729 int file_version = FILE_VERSION_1_2; /* last version of tape files */
732 /* always start with reliable default values (empty tape) */
735 /* default values (also for pre-1.2 tapes) with only the first player */
736 tape.player_participates[0] = TRUE;
737 for(i=1; i<MAX_PLAYERS; i++)
738 tape.player_participates[i] = FALSE;
740 /* at least one (default: the first) player participates in every tape */
741 num_participating_players = 1;
743 if (!(file = fopen(filename, "r")))
746 /* check file identifier */
747 fgets(cookie, MAX_LINE_LEN, file);
748 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
749 cookie[strlen(cookie) - 1] = '\0';
751 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
752 file_version = FILE_VERSION_1_0;
753 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
755 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
760 /* read chunk "HEAD" */
761 if (file_version >= FILE_VERSION_1_2)
763 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
764 if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
766 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
772 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
773 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
774 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
776 /* read header fields that are new since version 1.2 */
777 if (file_version >= FILE_VERSION_1_2)
779 byte store_participating_players = fgetc(file);
781 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
784 /* since version 1.2, tapes store which players participate in the tape */
785 num_participating_players = 0;
786 for(i=0; i<MAX_PLAYERS; i++)
788 tape.player_participates[i] = FALSE;
790 if (store_participating_players & (1 << i))
792 tape.player_participates[i] = TRUE;
793 num_participating_players++;
798 tape.level_nr = level_nr;
800 tape.changed = FALSE;
802 tape.recording = FALSE;
803 tape.playing = FALSE;
804 tape.pausing = FALSE;
806 /* read chunk "BODY" */
807 if (file_version >= FILE_VERSION_1_2)
809 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
810 if (strcmp(chunk, "BODY") ||
811 chunk_length != (num_participating_players + 1) * tape.length)
813 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
819 for(i=0; i<tape.length; i++)
821 if (i >= MAX_TAPELEN)
824 for(j=0; j<MAX_PLAYERS; j++)
826 tape.pos[i].action[j] = MV_NO_MOVING;
828 if (tape.player_participates[j])
829 tape.pos[i].action[j] = fgetc(file);
832 tape.pos[i].delay = fgetc(file);
834 if (file_version == FILE_VERSION_1_0)
836 /* eliminate possible diagonal moves in old tapes */
837 /* this is only for backward compatibility */
839 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
840 byte action = tape.pos[i].action[0];
841 int k, num_moves = 0;
845 if (action & joy_dir[k])
847 tape.pos[i + num_moves].action[0] = joy_dir[k];
849 tape.pos[i + num_moves].delay = 0;
858 tape.length += num_moves;
868 if (i != tape.length)
869 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
871 tape.length_seconds = GetTapeLength();
874 void SaveTape(int level_nr)
877 char *filename = getTapeFilename(level_nr);
879 boolean new_tape = TRUE;
880 byte store_participating_players;
881 int num_participating_players;
883 InitTapeDirectory(leveldir_current->filename);
885 /* if a tape still exists, ask to overwrite it */
886 if (access(filename, F_OK) == 0)
889 if (!Request("Replace old tape ?", REQ_ASK))
893 /* count number of players and set corresponding bits for compact storage */
894 store_participating_players = 0;
895 num_participating_players = 0;
896 for(i=0; i<MAX_PLAYERS; i++)
898 if (tape.player_participates[i])
900 num_participating_players++;
901 store_participating_players |= (1 << i);
905 if (!(file = fopen(filename, "w")))
907 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
911 fputs(TAPE_COOKIE, file); /* file identifier */
914 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
916 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
917 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
918 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
920 fputc(store_participating_players, file);
922 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
925 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
926 BYTE_ORDER_BIG_ENDIAN);
928 for(i=0; i<tape.length; i++)
932 for(j=0; j<MAX_PLAYERS; j++)
933 if (tape.player_participates[j])
934 fputc(tape.pos[i].action[j], file);
936 fputc(tape.pos[i].delay, file);
941 chmod(filename, TAPE_PERMS);
943 tape.changed = FALSE;
946 Request("tape saved !", REQ_CONFIRM);
949 void LoadScore(int level_nr)
952 char *filename = getScoreFilename(level_nr);
953 char cookie[MAX_LINE_LEN];
954 char line[MAX_LINE_LEN];
958 /* always start with reliable default values */
959 for(i=0; i<MAX_SCORE_ENTRIES; i++)
961 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
962 highscore[i].Score = 0;
965 if (!(file = fopen(filename, "r")))
968 /* check file identifier */
969 fgets(cookie, MAX_LINE_LEN, file);
970 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
971 cookie[strlen(cookie) - 1] = '\0';
973 if (strcmp(cookie, SCORE_COOKIE) != 0)
975 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
980 for(i=0; i<MAX_SCORE_ENTRIES; i++)
982 fscanf(file, "%d", &highscore[i].Score);
983 fgets(line, MAX_LINE_LEN, file);
985 if (line[strlen(line) - 1] == '\n')
986 line[strlen(line) - 1] = '\0';
988 for (line_ptr = line; *line_ptr; line_ptr++)
990 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
992 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
993 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1002 void SaveScore(int level_nr)
1005 char *filename = getScoreFilename(level_nr);
1008 InitScoreDirectory(leveldir_current->filename);
1010 if (!(file = fopen(filename, "w")))
1012 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1016 fprintf(file, "%s\n\n", SCORE_COOKIE);
1018 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1019 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1023 chmod(filename, SCORE_PERMS);
1026 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1027 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1028 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1029 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1030 #define TOKEN_STR_PLAYER_PREFIX "player_"
1032 #define TOKEN_VALUE_POSITION 30
1035 #define SETUP_TOKEN_PLAYER_NAME 0
1036 #define SETUP_TOKEN_SOUND 1
1037 #define SETUP_TOKEN_SOUND_LOOPS 2
1038 #define SETUP_TOKEN_SOUND_MUSIC 3
1039 #define SETUP_TOKEN_SOUND_SIMPLE 4
1042 #define SETUP_TOKEN_TOONS 5
1043 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1046 #define SETUP_TOKEN_SCROLL_DELAY 5
1047 #define SETUP_TOKEN_SOFT_SCROLLING 6
1048 #define SETUP_TOKEN_FADING 7
1049 #define SETUP_TOKEN_AUTORECORD 8
1050 #define SETUP_TOKEN_QUICK_DOORS 9
1051 #define SETUP_TOKEN_TEAM_MODE 10
1052 #define SETUP_TOKEN_HANDICAP 11
1053 #define SETUP_TOKEN_TIME_LIMIT 12
1056 #define SETUP_TOKEN_USE_JOYSTICK 13
1057 #define SETUP_TOKEN_JOY_DEVICE_NAME 14
1058 #define SETUP_TOKEN_JOY_XLEFT 15
1059 #define SETUP_TOKEN_JOY_XMIDDLE 16
1060 #define SETUP_TOKEN_JOY_XRIGHT 17
1061 #define SETUP_TOKEN_JOY_YUPPER 18
1062 #define SETUP_TOKEN_JOY_YMIDDLE 19
1063 #define SETUP_TOKEN_JOY_YLOWER 20
1064 #define SETUP_TOKEN_JOY_SNAP 21
1065 #define SETUP_TOKEN_JOY_BOMB 22
1066 #define SETUP_TOKEN_KEY_LEFT 23
1067 #define SETUP_TOKEN_KEY_RIGHT 24
1068 #define SETUP_TOKEN_KEY_UP 25
1069 #define SETUP_TOKEN_KEY_DOWN 26
1070 #define SETUP_TOKEN_KEY_SNAP 27
1071 #define SETUP_TOKEN_KEY_BOMB 28
1073 /* level directory info */
1074 #define LEVELINFO_TOKEN_NAME 29
1075 #define LEVELINFO_TOKEN_NAME_SHORT 30
1076 #define LEVELINFO_TOKEN_NAME_SORTING 31
1077 #define LEVELINFO_TOKEN_AUTHOR 32
1078 #define LEVELINFO_TOKEN_IMPORTED_FROM 33
1079 #define LEVELINFO_TOKEN_LEVELS 34
1080 #define LEVELINFO_TOKEN_FIRST_LEVEL 35
1081 #define LEVELINFO_TOKEN_SORT_PRIORITY 36
1082 #define LEVELINFO_TOKEN_LEVEL_GROUP 37
1083 #define LEVELINFO_TOKEN_READONLY 38
1085 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1086 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_TIME_LIMIT
1088 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1089 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1091 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1092 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1094 #define TYPE_BOOLEAN 1
1095 #define TYPE_SWITCH 2
1096 #define TYPE_KEYSYM 3
1097 #define TYPE_INTEGER 4
1098 #define TYPE_STRING 5
1100 static struct SetupInfo si;
1101 static struct SetupInputInfo sii;
1102 static struct LevelDirInfo ldi;
1111 { TYPE_STRING, &si.player_name, "player_name" },
1112 { TYPE_SWITCH, &si.sound, "sound" },
1113 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1114 { TYPE_SWITCH, &si.sound_music, "background_music" },
1115 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1118 { TYPE_SWITCH, &si.toons, "toons" },
1119 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1122 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1123 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1124 { TYPE_SWITCH, &si.fading, "screen_fading" },
1125 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1126 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1127 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1128 { TYPE_SWITCH, &si.handicap, "handicap" },
1129 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1132 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1133 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1134 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1135 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1136 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1137 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1138 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1139 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1140 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1141 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1142 { TYPE_KEYSYM, &sii.key.left, ".key.move_left" },
1143 { TYPE_KEYSYM, &sii.key.right, ".key.move_right" },
1144 { TYPE_KEYSYM, &sii.key.up, ".key.move_up" },
1145 { TYPE_KEYSYM, &sii.key.down, ".key.move_down" },
1146 { TYPE_KEYSYM, &sii.key.snap, ".key.snap_field" },
1147 { TYPE_KEYSYM, &sii.key.bomb, ".key.place_bomb" },
1149 /* level directory info */
1150 { TYPE_STRING, &ldi.name, "name" },
1151 { TYPE_STRING, &ldi.name_short, "name_short" },
1152 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1153 { TYPE_STRING, &ldi.author, "author" },
1154 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1155 { TYPE_INTEGER, &ldi.levels, "levels" },
1156 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1157 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1158 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1159 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1162 static char *string_tolower(char *s)
1164 static char s_lower[100];
1167 if (strlen(s) >= 100)
1172 for (i=0; i<strlen(s_lower); i++)
1173 s_lower[i] = tolower(s_lower[i]);
1178 static int get_string_integer_value(char *s)
1180 static char *number_text[][3] =
1182 { "0", "zero", "null", },
1183 { "1", "one", "first" },
1184 { "2", "two", "second" },
1185 { "3", "three", "third" },
1186 { "4", "four", "fourth" },
1187 { "5", "five", "fifth" },
1188 { "6", "six", "sixth" },
1189 { "7", "seven", "seventh" },
1190 { "8", "eight", "eighth" },
1191 { "9", "nine", "ninth" },
1192 { "10", "ten", "tenth" },
1193 { "11", "eleven", "eleventh" },
1194 { "12", "twelve", "twelfth" },
1199 for (i=0; i<13; i++)
1201 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1207 static boolean get_string_boolean_value(char *s)
1209 if (strcmp(string_tolower(s), "true") == 0 ||
1210 strcmp(string_tolower(s), "yes") == 0 ||
1211 strcmp(string_tolower(s), "on") == 0 ||
1212 get_string_integer_value(s) == 1)
1218 static char *getFormattedSetupEntry(char *token, char *value)
1221 static char entry[MAX_LINE_LEN];
1223 sprintf(entry, "%s:", token);
1224 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1228 strcat(entry, value);
1233 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1235 if (!setup_file_list)
1238 if (setup_file_list->token)
1239 free(setup_file_list->token);
1240 if (setup_file_list->value)
1241 free(setup_file_list->value);
1242 if (setup_file_list->next)
1243 freeSetupFileList(setup_file_list->next);
1244 free(setup_file_list);
1247 static struct SetupFileList *newSetupFileList(char *token, char *value)
1249 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1251 new->token = checked_malloc(strlen(token) + 1);
1252 strcpy(new->token, token);
1254 new->value = checked_malloc(strlen(value) + 1);
1255 strcpy(new->value, value);
1262 static char *getTokenValue(struct SetupFileList *setup_file_list,
1265 if (!setup_file_list)
1268 if (strcmp(setup_file_list->token, token) == 0)
1269 return setup_file_list->value;
1271 return getTokenValue(setup_file_list->next, token);
1274 static void setTokenValue(struct SetupFileList *setup_file_list,
1275 char *token, char *value)
1277 if (!setup_file_list)
1280 if (strcmp(setup_file_list->token, token) == 0)
1282 free(setup_file_list->value);
1283 setup_file_list->value = checked_malloc(strlen(value) + 1);
1284 strcpy(setup_file_list->value, value);
1286 else if (setup_file_list->next == NULL)
1287 setup_file_list->next = newSetupFileList(token, value);
1289 setTokenValue(setup_file_list->next, token, value);
1293 static void printSetupFileList(struct SetupFileList *setup_file_list)
1295 if (!setup_file_list)
1298 printf("token: '%s'\n", setup_file_list->token);
1299 printf("value: '%s'\n", setup_file_list->value);
1301 printSetupFileList(setup_file_list->next);
1305 static struct SetupFileList *loadSetupFileList(char *filename)
1308 char line[MAX_LINE_LEN];
1309 char *token, *value, *line_ptr;
1310 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1311 struct SetupFileList *first_valid_list_entry;
1315 if (!(file = fopen(filename, "r")))
1317 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1323 /* read next line of input file */
1324 if (!fgets(line, MAX_LINE_LEN, file))
1327 /* cut trailing comment or whitespace from input line */
1328 for (line_ptr = line; *line_ptr; line_ptr++)
1330 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1337 /* cut trailing whitespaces from input line */
1338 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1339 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1342 /* ignore empty lines */
1346 line_len = strlen(line);
1348 /* cut leading whitespaces from token */
1349 for (token = line; *token; token++)
1350 if (*token != ' ' && *token != '\t')
1353 /* find end of token */
1354 for (line_ptr = token; *line_ptr; line_ptr++)
1356 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1363 if (line_ptr < line + line_len)
1364 value = line_ptr + 1;
1368 /* cut leading whitespaces from value */
1369 for (; *value; value++)
1370 if (*value != ' ' && *value != '\t')
1373 if (*token && *value)
1374 setTokenValue(setup_file_list, token, value);
1379 first_valid_list_entry = setup_file_list->next;
1381 /* free empty list header */
1382 setup_file_list->next = NULL;
1383 freeSetupFileList(setup_file_list);
1385 if (first_valid_list_entry == NULL)
1386 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1388 return first_valid_list_entry;
1391 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1394 if (!setup_file_list)
1397 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1399 if (strcmp(setup_file_list->value, identifier) != 0)
1401 Error(ERR_WARN, "configuration file has wrong version");
1408 if (setup_file_list->next)
1409 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1412 Error(ERR_WARN, "configuration file has no version information");
1417 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1419 ldi->filename = NULL;
1420 ldi->fullpath = NULL;
1421 ldi->basepath = NULL;
1422 ldi->name = getStringCopy(ANONYMOUS_NAME);
1423 ldi->name_short = NULL;
1424 ldi->name_sorting = NULL;
1425 ldi->author = getStringCopy(ANONYMOUS_NAME);
1426 ldi->imported_from = NULL;
1428 ldi->first_level = 0;
1429 ldi->last_level = 0;
1430 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1431 ldi->level_group = FALSE;
1432 ldi->parent_link = FALSE;
1433 ldi->user_defined = FALSE;
1434 ldi->readonly = TRUE;
1436 ldi->class_desc = NULL;
1437 ldi->handicap_level = 0;
1439 ldi->cl_cursor = -1;
1441 ldi->node_parent = NULL;
1442 ldi->node_group = NULL;
1446 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1447 struct LevelDirInfo *parent)
1451 setLevelDirInfoToDefaults(ldi);
1455 /* first copy all values from the parent structure ... */
1458 /* ... then set all fields to default that cannot be inherited from parent.
1459 This is especially important for all those fields that can be set from
1460 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1461 calls 'free()' for all already set token values which requires that no
1462 other structure's pointer may point to them!
1465 ldi->filename = NULL;
1466 ldi->fullpath = NULL;
1467 ldi->basepath = NULL;
1468 ldi->name = getStringCopy(ANONYMOUS_NAME);
1469 ldi->name_short = NULL;
1470 ldi->name_sorting = NULL;
1471 ldi->author = getStringCopy(parent->author);
1472 ldi->imported_from = getStringCopy(parent->imported_from);
1474 ldi->level_group = FALSE;
1475 ldi->parent_link = FALSE;
1477 ldi->node_parent = parent;
1478 ldi->node_group = NULL;
1482 static void setSetupInfoToDefaults(struct SetupInfo *si)
1486 si->player_name = getStringCopy(getLoginName());
1489 si->sound_loops = TRUE;
1490 si->sound_music = TRUE;
1491 si->sound_simple = TRUE;
1493 si->double_buffering = TRUE;
1494 si->direct_draw = !si->double_buffering;
1495 si->scroll_delay = TRUE;
1496 si->soft_scrolling = TRUE;
1498 si->autorecord = TRUE;
1499 si->quick_doors = FALSE;
1500 si->team_mode = FALSE;
1501 si->handicap = TRUE;
1502 si->time_limit = TRUE;
1504 for (i=0; i<MAX_PLAYERS; i++)
1506 si->input[i].use_joystick = FALSE;
1507 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1508 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1509 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1510 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1511 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1512 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1513 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1514 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1515 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1516 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KEY_UNDEFINDED);
1517 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
1518 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KEY_UNDEFINDED);
1519 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KEY_UNDEFINDED);
1520 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KEY_UNDEFINDED);
1521 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KEY_UNDEFINDED);
1525 static void setSetupInfo(int token_nr, char *token_value)
1527 int token_type = token_info[token_nr].type;
1528 void *setup_value = token_info[token_nr].value;
1530 if (token_value == NULL)
1533 /* set setup field to corresponding token value */
1538 *(boolean *)setup_value = get_string_boolean_value(token_value);
1542 *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
1546 *(int *)setup_value = get_string_integer_value(token_value);
1550 if (*(char **)setup_value != NULL)
1551 free(*(char **)setup_value);
1552 *(char **)setup_value = getStringCopy(token_value);
1560 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1564 if (!setup_file_list)
1567 /* handle global setup values */
1569 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1570 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1573 /* handle player specific setup values */
1574 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1578 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1580 sii = setup.input[pnr];
1581 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1583 char full_token[100];
1585 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1586 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1588 setup.input[pnr] = sii;
1592 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1594 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1595 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1598 if (entry1->parent_link || entry2->parent_link)
1599 compare_result = (entry1->parent_link ? -1 : +1);
1600 else if (entry1->sort_priority == entry2->sort_priority)
1602 char *name1 = getStringToLower(entry1->name_sorting);
1603 char *name2 = getStringToLower(entry2->name_sorting);
1605 compare_result = strcmp(name1, name2);
1610 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1611 compare_result = entry1->sort_priority - entry2->sort_priority;
1613 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1615 return compare_result;
1618 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1620 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1622 setLevelDirInfoToDefaults(leveldir_new);
1624 leveldir_new->node_parent = node_parent;
1625 leveldir_new->parent_link = TRUE;
1627 leveldir_new->name = ".. (parent directory)";
1628 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1629 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1631 leveldir_new->filename = "..";
1632 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1634 leveldir_new->sort_priority = node_parent->sort_priority;
1635 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1637 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1640 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1641 struct LevelDirInfo *node_parent,
1642 char *level_directory)
1645 struct dirent *dir_entry;
1646 boolean valid_entry_found = FALSE;
1648 if ((dir = opendir(level_directory)) == NULL)
1650 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1654 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1656 struct SetupFileList *setup_file_list = NULL;
1657 struct stat file_status;
1658 char *directory_name = dir_entry->d_name;
1659 char *directory_path = getPath2(level_directory, directory_name);
1660 char *filename = NULL;
1662 /* skip entries for current and parent directory */
1663 if (strcmp(directory_name, ".") == 0 ||
1664 strcmp(directory_name, "..") == 0)
1666 free(directory_path);
1670 /* find out if directory entry is itself a directory */
1671 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1672 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1674 free(directory_path);
1678 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1679 setup_file_list = loadSetupFileList(filename);
1681 if (setup_file_list)
1683 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1686 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1687 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1689 /* set all structure fields according to the token/value pairs */
1690 ldi = *leveldir_new;
1691 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1692 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1693 *leveldir_new = ldi;
1695 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1697 if (leveldir_new->name_short == NULL)
1698 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1700 if (leveldir_new->name_sorting == NULL)
1701 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1703 leveldir_new->filename = getStringCopy(directory_name);
1705 if (node_parent == NULL) /* top level group */
1707 leveldir_new->basepath = level_directory;
1708 leveldir_new->fullpath = leveldir_new->filename;
1710 else /* sub level group */
1712 leveldir_new->basepath = node_parent->basepath;
1713 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1717 if (leveldir_new->levels < 1)
1718 leveldir_new->levels = 1;
1720 leveldir_new->last_level =
1721 leveldir_new->first_level + leveldir_new->levels - 1;
1723 leveldir_new->user_defined =
1724 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1726 leveldir_new->color = LEVELCOLOR(leveldir_new);
1727 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1729 leveldir_new->handicap_level = /* set handicap to default value */
1730 (leveldir_new->user_defined ?
1731 leveldir_new->last_level :
1732 leveldir_new->first_level);
1734 pushLevelDirInfo(node_first, leveldir_new);
1736 freeSetupFileList(setup_file_list);
1737 valid_entry_found = TRUE;
1739 if (leveldir_new->level_group)
1741 /* create node to link back to current level directory */
1742 createParentLevelDirNode(leveldir_new);
1744 /* step into sub-directory and look for more level series */
1745 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1746 leveldir_new, directory_path);
1750 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1752 free(directory_path);
1758 if (!valid_entry_found)
1759 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1763 void LoadLevelInfo()
1765 InitUserLevelDirectory(getLoginName());
1767 DrawInitText("Loading level series:", 120, FC_GREEN);
1769 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1770 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1772 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1774 if (leveldir_first == NULL)
1775 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1777 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1780 dumpLevelDirInfo(leveldir_first, 0);
1784 static void SaveUserLevelInfo()
1790 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1792 if (!(file = fopen(filename, "w")))
1794 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1799 /* always start with reliable default values */
1800 setLevelDirInfoToDefaults(&ldi);
1802 ldi.name = getLoginName();
1803 ldi.author = getRealName();
1805 ldi.first_level = 1;
1806 ldi.sort_priority = LEVELCLASS_USER_START;
1807 ldi.readonly = FALSE;
1809 fprintf(file, "%s\n\n",
1810 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1812 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1813 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1814 i != LEVELINFO_TOKEN_NAME_SORTING &&
1815 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1816 fprintf(file, "%s\n", getSetupLine("", i));
1821 chmod(filename, SETUP_PERMS);
1827 struct SetupFileList *setup_file_list = NULL;
1829 /* always start with reliable default values */
1830 setSetupInfoToDefaults(&setup);
1832 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1834 setup_file_list = loadSetupFileList(filename);
1836 if (setup_file_list)
1838 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1839 decodeSetupFileList(setup_file_list);
1841 setup.direct_draw = !setup.double_buffering;
1843 freeSetupFileList(setup_file_list);
1845 /* needed to work around problems with fixed length strings */
1846 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1847 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1848 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1850 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1852 strcpy(new_name, setup.player_name);
1853 free(setup.player_name);
1854 setup.player_name = new_name;
1858 Error(ERR_WARN, "using default setup values");
1863 static char *getSetupLine(char *prefix, int token_nr)
1866 static char entry[MAX_LINE_LEN];
1867 int token_type = token_info[token_nr].type;
1868 void *setup_value = token_info[token_nr].value;
1869 char *token_text = token_info[token_nr].text;
1871 /* start with the prefix, token and some spaces to format output line */
1872 sprintf(entry, "%s%s:", prefix, token_text);
1873 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1876 /* continue with the token's value (which can have different types) */
1880 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1884 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1889 KeySym keysym = *(KeySym *)setup_value;
1890 char *keyname = getKeyNameFromKeySym(keysym);
1892 strcat(entry, getX11KeyNameFromKeySym(keysym));
1893 for (i=strlen(entry); i<50; i++)
1896 /* add comment, if useful */
1897 if (strcmp(keyname, "(undefined)") != 0 &&
1898 strcmp(keyname, "(unknown)") != 0)
1900 strcat(entry, "# ");
1901 strcat(entry, keyname);
1908 char buffer[MAX_LINE_LEN];
1910 sprintf(buffer, "%d", *(int *)setup_value);
1911 strcat(entry, buffer);
1916 strcat(entry, *(char **)setup_value);
1932 InitUserDataDirectory();
1934 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1936 if (!(file = fopen(filename, "w")))
1938 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1943 fprintf(file, "%s\n",
1944 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1945 fprintf(file, "\n");
1947 /* handle global setup values */
1949 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1951 fprintf(file, "%s\n", getSetupLine("", i));
1953 /* just to make things nicer :) */
1954 if (i == SETUP_TOKEN_PLAYER_NAME)
1955 fprintf(file, "\n");
1958 /* handle player specific setup values */
1959 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1963 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1964 fprintf(file, "\n");
1966 sii = setup.input[pnr];
1967 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1968 fprintf(file, "%s\n", getSetupLine(prefix, i));
1974 chmod(filename, SETUP_PERMS);
1977 void LoadLevelSetup_LastSeries()
1980 struct SetupFileList *level_setup_list = NULL;
1982 /* always start with reliable default values */
1983 leveldir_current = leveldir_first;
1985 /* ----------------------------------------------------------------------- */
1986 /* ~/.rocksndiamonds/levelsetup.conf */
1987 /* ----------------------------------------------------------------------- */
1989 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1991 if ((level_setup_list = loadSetupFileList(filename)))
1993 char *last_level_series =
1994 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1996 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
1997 if (leveldir_current == NULL)
1998 leveldir_current = leveldir_first;
2000 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2002 freeSetupFileList(level_setup_list);
2005 Error(ERR_WARN, "using default setup values");
2010 void SaveLevelSetup_LastSeries()
2013 char *level_subdir = leveldir_current->filename;
2016 /* ----------------------------------------------------------------------- */
2017 /* ~/.rocksndiamonds/levelsetup.conf */
2018 /* ----------------------------------------------------------------------- */
2020 InitUserDataDirectory();
2022 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2024 if (!(file = fopen(filename, "w")))
2026 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2031 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2032 LEVELSETUP_COOKIE));
2033 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2039 chmod(filename, SETUP_PERMS);
2042 static void checkSeriesInfo()
2044 static char *level_directory = NULL;
2046 struct dirent *dir_entry;
2048 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2050 level_directory = getPath2((leveldir_current->user_defined ?
2051 getUserLevelDir("") :
2052 options.level_directory),
2053 leveldir_current->filename);
2055 if ((dir = opendir(level_directory)) == NULL)
2057 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2061 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2063 if (strlen(dir_entry->d_name) > 4 &&
2064 dir_entry->d_name[3] == '.' &&
2065 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2067 char levelnum_str[4];
2070 strncpy(levelnum_str, dir_entry->d_name, 3);
2071 levelnum_str[3] = '\0';
2073 levelnum_value = atoi(levelnum_str);
2075 if (levelnum_value < leveldir_current->first_level)
2077 Error(ERR_WARN, "additional level %d found", levelnum_value);
2078 leveldir_current->first_level = levelnum_value;
2080 else if (levelnum_value > leveldir_current->last_level)
2082 Error(ERR_WARN, "additional level %d found", levelnum_value);
2083 leveldir_current->last_level = levelnum_value;
2091 void LoadLevelSetup_SeriesInfo()
2094 struct SetupFileList *level_setup_list = NULL;
2095 char *level_subdir = leveldir_current->filename;
2097 /* always start with reliable default values */
2098 level_nr = leveldir_current->first_level;
2100 checkSeriesInfo(leveldir_current);
2102 /* ----------------------------------------------------------------------- */
2103 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2104 /* ----------------------------------------------------------------------- */
2106 level_subdir = leveldir_current->filename;
2108 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2110 if ((level_setup_list = loadSetupFileList(filename)))
2114 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2118 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)
2123 level_nr = leveldir_current->last_level;
2126 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2130 int level_nr = atoi(token_value);
2132 if (level_nr < leveldir_current->first_level)
2133 level_nr = leveldir_current->first_level;
2134 if (level_nr > leveldir_current->last_level + 1)
2135 level_nr = leveldir_current->last_level;
2137 if (leveldir_current->user_defined)
2138 level_nr = leveldir_current->last_level;
2140 leveldir_current->handicap_level = level_nr;
2143 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2145 freeSetupFileList(level_setup_list);
2148 Error(ERR_WARN, "using default setup values");
2153 void SaveLevelSetup_SeriesInfo()
2156 char *level_subdir = leveldir_current->filename;
2157 char *level_nr_str = int2str(level_nr, 0);
2158 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2161 /* ----------------------------------------------------------------------- */
2162 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2163 /* ----------------------------------------------------------------------- */
2165 InitLevelSetupDirectory(level_subdir);
2167 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2169 if (!(file = fopen(filename, "w")))
2171 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2176 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2177 LEVELSETUP_COOKIE));
2178 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2180 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2181 handicap_level_str));
2186 chmod(filename, SETUP_PERMS);
2190 void initErrorFile()
2194 InitUserDataDirectory();
2196 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2201 FILE *openErrorFile()
2206 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2207 error_file = fopen(filename, "a");
2213 void dumpErrorFile()
2218 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2219 error_file = fopen(filename, "r");
2222 if (error_file != NULL)
2224 while (!feof(error_file))
2225 fputc(fgetc(error_file), stderr);