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 ***********************************************************/
18 #include "libgame/libgame.h"
25 #define MAX_FILENAME_LEN 256 /* maximal filename length */
26 #define MAX_LINE_LEN 1000 /* maximal input line length */
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
29 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
30 #define 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 */
49 #if !defined(PLATFORM_MSDOS)
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"
69 #if !defined(PLATFORM_UNIX)
70 #define ERROR_FILENAME "error.out"
73 #if defined(PLATFORM_WIN32)
75 #define S_IRGRP S_IRUSR
78 #define S_IROTH S_IRUSR
81 #define S_IWGRP S_IWUSR
84 #define S_IWOTH S_IWUSR
87 #define S_IXGRP S_IXUSR
90 #define S_IXOTH S_IXUSR
92 #endif /* PLATFORM_WIN32 */
94 /* file permissions for newly written files */
95 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
96 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
97 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
98 #define USERDATA_DIR_MODE (MODE_R_ALL | MODE_X_ALL | S_IWUSR)
99 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
100 #define SCORE_PERMS LEVEL_PERMS
101 #define TAPE_PERMS LEVEL_PERMS
102 #define SETUP_PERMS LEVEL_PERMS
104 /* sort priorities of level series (also used as level series classes) */
105 #define LEVELCLASS_TUTORIAL_START 10
106 #define LEVELCLASS_TUTORIAL_END 99
107 #define LEVELCLASS_CLASSICS_START 100
108 #define LEVELCLASS_CLASSICS_END 199
109 #define LEVELCLASS_CONTRIBUTION_START 200
110 #define LEVELCLASS_CONTRIBUTION_END 299
111 #define LEVELCLASS_USER_START 300
112 #define LEVELCLASS_USER_END 399
113 #define LEVELCLASS_BD_START 400
114 #define LEVELCLASS_BD_END 499
115 #define LEVELCLASS_EM_START 500
116 #define LEVELCLASS_EM_END 599
117 #define LEVELCLASS_SP_START 600
118 #define LEVELCLASS_SP_END 699
119 #define LEVELCLASS_DX_START 700
120 #define LEVELCLASS_DX_END 799
122 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
123 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
124 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
125 #define LEVELCLASS_USER LEVELCLASS_USER_START
126 #define LEVELCLASS_BD LEVELCLASS_BD_START
127 #define LEVELCLASS_EM LEVELCLASS_EM_START
128 #define LEVELCLASS_SP LEVELCLASS_SP_START
129 #define LEVELCLASS_DX LEVELCLASS_DX_START
131 #define LEVELCLASS_UNDEFINED 999
133 #define NUM_LEVELCLASS_DESC 8
134 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
146 #define IS_LEVELCLASS_TUTORIAL(p) \
147 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
148 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
149 #define IS_LEVELCLASS_CLASSICS(p) \
150 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
151 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
152 #define IS_LEVELCLASS_CONTRIBUTION(p) \
153 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
154 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
155 #define IS_LEVELCLASS_USER(p) \
156 ((p)->sort_priority >= LEVELCLASS_USER_START && \
157 (p)->sort_priority <= LEVELCLASS_USER_END)
158 #define IS_LEVELCLASS_BD(p) \
159 ((p)->sort_priority >= LEVELCLASS_BD_START && \
160 (p)->sort_priority <= LEVELCLASS_BD_END)
161 #define IS_LEVELCLASS_EM(p) \
162 ((p)->sort_priority >= LEVELCLASS_EM_START && \
163 (p)->sort_priority <= LEVELCLASS_EM_END)
164 #define IS_LEVELCLASS_SP(p) \
165 ((p)->sort_priority >= LEVELCLASS_SP_START && \
166 (p)->sort_priority <= LEVELCLASS_SP_END)
167 #define IS_LEVELCLASS_DX(p) \
168 ((p)->sort_priority >= LEVELCLASS_DX_START && \
169 (p)->sort_priority <= LEVELCLASS_DX_END)
171 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
172 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
173 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
174 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
175 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
176 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
177 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
178 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
179 LEVELCLASS_UNDEFINED)
181 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
182 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
183 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
184 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
185 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
186 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
187 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
188 IS_LEVELCLASS_USER(n) ? FC_RED : \
191 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
192 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
193 IS_LEVELCLASS_BD(n) ? 2 : \
194 IS_LEVELCLASS_EM(n) ? 3 : \
195 IS_LEVELCLASS_SP(n) ? 4 : \
196 IS_LEVELCLASS_DX(n) ? 5 : \
197 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
198 IS_LEVELCLASS_USER(n) ? 7 : \
201 char *getLevelClassDescription(struct LevelDirInfo *ldi)
203 int position = ldi->sort_priority / 100;
205 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
206 return levelclass_desc[position];
208 return "Unknown Level Class";
211 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
212 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
214 char *getUserDataDir()
216 static char *userdata_dir = NULL;
220 char *home_dir = getHomeDir();
221 char *data_dir = USERDATA_DIRECTORY;
223 userdata_dir = getPath2(home_dir, data_dir);
229 static char *getSetupDir()
231 return getUserDataDir();
234 static char *getUserLevelDir(char *level_subdir)
236 static char *userlevel_dir = NULL;
237 char *data_dir = getUserDataDir();
238 char *userlevel_subdir = LEVELS_DIRECTORY;
243 if (strlen(level_subdir) > 0)
244 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
246 userlevel_dir = getPath2(data_dir, userlevel_subdir);
248 return userlevel_dir;
251 static char *getTapeDir(char *level_subdir)
253 static char *tape_dir = NULL;
254 char *data_dir = getUserDataDir();
255 char *tape_subdir = TAPES_DIRECTORY;
260 if (strlen(level_subdir) > 0)
261 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
263 tape_dir = getPath2(data_dir, tape_subdir);
268 static char *getScoreDir(char *level_subdir)
270 static char *score_dir = NULL;
271 char *data_dir = options.rw_base_directory;
272 char *score_subdir = SCORES_DIRECTORY;
277 if (strlen(level_subdir) > 0)
278 score_dir = getPath3(data_dir, score_subdir, level_subdir);
280 score_dir = getPath2(data_dir, score_subdir);
285 static char *getLevelSetupDir(char *level_subdir)
287 static char *levelsetup_dir = NULL;
288 char *data_dir = getUserDataDir();
289 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
292 free(levelsetup_dir);
294 if (strlen(level_subdir) > 0)
295 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
297 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
299 return levelsetup_dir;
302 static char *getLevelFilename(int nr)
304 static char *filename = NULL;
305 char basename[MAX_FILENAME_LEN];
307 if (filename != NULL)
310 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
311 filename = getPath3((leveldir_current->user_defined ?
312 getUserLevelDir("") :
313 options.level_directory),
314 leveldir_current->fullpath,
320 static char *getTapeFilename(int nr)
322 static char *filename = NULL;
323 char basename[MAX_FILENAME_LEN];
325 if (filename != NULL)
328 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
329 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
334 static char *getScoreFilename(int nr)
336 static char *filename = NULL;
337 char basename[MAX_FILENAME_LEN];
339 if (filename != NULL)
342 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
343 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
348 static void createDirectory(char *dir, char *text)
350 if (access(dir, F_OK) != 0)
351 #if defined(PLATFORM_WIN32)
354 if (mkdir(dir, USERDATA_DIR_MODE) != 0)
356 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
359 static void InitUserDataDirectory()
361 createDirectory(getUserDataDir(), "user data");
364 static void InitTapeDirectory(char *level_subdir)
366 createDirectory(getUserDataDir(), "user data");
367 createDirectory(getTapeDir(""), "main tape");
368 createDirectory(getTapeDir(level_subdir), "level tape");
371 static void InitScoreDirectory(char *level_subdir)
373 createDirectory(getScoreDir(""), "main score");
374 createDirectory(getScoreDir(level_subdir), "level score");
377 static void InitUserLevelDirectory(char *level_subdir)
379 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
381 createDirectory(getUserDataDir(), "user data");
382 createDirectory(getUserLevelDir(""), "main user level");
383 createDirectory(getUserLevelDir(level_subdir), "user level");
389 static void InitLevelSetupDirectory(char *level_subdir)
391 createDirectory(getUserDataDir(), "user data");
392 createDirectory(getLevelSetupDir(""), "main level setup");
393 createDirectory(getLevelSetupDir(level_subdir), "level setup");
396 static void setLevelInfoToDefaults()
400 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
401 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
403 for(x=0; x<MAX_LEV_FIELDX; x++)
404 for(y=0; y<MAX_LEV_FIELDY; y++)
405 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
408 level.gems_needed = 0;
409 level.amoeba_speed = 10;
410 level.time_magic_wall = 10;
411 level.time_wheel = 10;
412 level.time_light = 10;
413 level.time_timegate = 10;
414 level.amoeba_content = EL_DIAMANT;
415 level.double_speed = FALSE;
416 level.gravity = FALSE;
418 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
419 level.name[i] = '\0';
420 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
421 level.author[i] = '\0';
423 strcpy(level.name, NAMELESS_LEVEL_NAME);
424 strcpy(level.author, ANONYMOUS_NAME);
426 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
429 level.num_yam_contents = STD_ELEMENT_CONTENTS;
430 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
433 level.yam_content[i][x][y] = EL_FELSBROCKEN;
435 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
436 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
437 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
439 BorderElement = EL_BETON;
441 /* try to determine better author name than 'anonymous' */
442 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
444 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
445 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
449 switch (LEVELCLASS(leveldir_current))
451 case LEVELCLASS_TUTORIAL:
452 strcpy(level.author, PROGRAM_AUTHOR_STRING);
455 case LEVELCLASS_CONTRIBUTION:
456 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
457 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
460 case LEVELCLASS_USER:
461 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
462 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
466 /* keep default value */
472 static int checkLevelElement(int element)
474 if (element >= EL_FIRST_RUNTIME_EL)
476 Error(ERR_WARN, "invalid level element %d", element);
477 element = EL_CHAR_FRAGE;
483 void LoadLevel(int level_nr)
486 char *filename = getLevelFilename(level_nr);
487 char cookie[MAX_LINE_LEN];
488 char chunk[CHUNK_ID_LEN + 1];
489 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
490 int file_version = FILE_VERSION_1_4; /* last version of level files */
494 /* always start with reliable default values */
495 setLevelInfoToDefaults();
497 if (!(file = fopen(filename, "r")))
499 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
503 /* check file identifier */
504 fgets(cookie, MAX_LINE_LEN, file);
505 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
506 cookie[strlen(cookie) - 1] = '\0';
508 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
509 file_version = FILE_VERSION_1_0;
510 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
511 file_version = FILE_VERSION_1_2;
512 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
514 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
519 /* read chunk "HEAD" */
520 if (file_version >= FILE_VERSION_1_2)
522 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
523 if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
525 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
531 lev_fieldx = level.fieldx = fgetc(file);
532 lev_fieldy = level.fieldy = fgetc(file);
534 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
535 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
537 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
538 level.name[i] = fgetc(file);
539 level.name[MAX_LEVEL_NAME_LEN] = 0;
541 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
542 level.score[i] = fgetc(file);
544 level.num_yam_contents = STD_ELEMENT_CONTENTS;
545 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
551 if (i < STD_ELEMENT_CONTENTS)
552 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
554 level.yam_content[i][x][y] = EL_LEERRAUM;
559 level.amoeba_speed = fgetc(file);
560 level.time_magic_wall = fgetc(file);
561 level.time_wheel = fgetc(file);
562 level.amoeba_content = checkLevelElement(fgetc(file));
563 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
564 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
566 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
568 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
571 if (file_version >= FILE_VERSION_1_2)
573 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
575 /* look for optional author chunk */
576 if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
578 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
579 level.author[i] = fgetc(file);
580 level.author[MAX_LEVEL_NAME_LEN] = 0;
582 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
585 /* look for optional content chunk */
586 if (strcmp(chunk, "CONT") == 0 &&
587 chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
590 level.num_yam_contents = fgetc(file);
594 if (level.num_yam_contents < 1 ||
595 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
598 printf("WARNING: num_yam_contents == %d (corrected)\n",
599 level.num_yam_contents);
601 level.num_yam_contents = STD_ELEMENT_CONTENTS;
604 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
607 level.yam_content[i][x][y] =
608 checkLevelElement(encoding_16bit ?
609 getFile16BitInteger(file,
610 BYTE_ORDER_BIG_ENDIAN) :
613 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
616 /* next check body chunk identifier and chunk length */
617 if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
619 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
625 /* clear all other level fields (needed if resized in level editor later) */
626 for(x=0; x<MAX_LEV_FIELDX; x++)
627 for(y=0; y<MAX_LEV_FIELDY; y++)
628 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
630 /* now read in the valid level fields from level file */
631 for(y=0; y<lev_fieldy; y++)
632 for(x=0; x<lev_fieldx; x++)
633 Feld[x][y] = Ur[x][y] =
634 checkLevelElement(encoding_16bit ?
635 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
640 /* player was faster than monsters in pre-1.0 levels */
641 if (file_version == FILE_VERSION_1_0 &&
642 IS_LEVELCLASS_CONTRIBUTION(leveldir_current))
644 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
645 Error(ERR_WARN, "using high speed movement for player");
646 level.double_speed = TRUE;
649 /* determine border element for this level */
653 void SaveLevel(int level_nr)
656 char *filename = getLevelFilename(level_nr);
657 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
658 char *oldest_possible_cookie;
661 if (!(file = fopen(filename, "w")))
663 Error(ERR_WARN, "cannot save level file '%s'", filename);
667 /* check yam content for 16-bit elements */
668 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
671 if (level.yam_content[i][x][y] > 255)
672 encoding_16bit = TRUE;
674 /* check level field for 16-bit elements */
675 for(y=0; y<lev_fieldy; y++)
676 for(x=0; x<lev_fieldx; x++)
678 encoding_16bit = TRUE;
680 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
682 fputs(oldest_possible_cookie, file); /* file identifier */
685 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
687 fputc(level.fieldx, file);
688 fputc(level.fieldy, file);
690 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
691 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
693 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
694 fputc(level.name[i], file);
695 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
696 fputc(level.score[i], file);
697 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
700 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
701 fputc(level.amoeba_speed, file);
702 fputc(level.time_magic_wall, file);
703 fputc(level.time_wheel, file);
704 fputc(level.amoeba_content, file);
705 fputc((level.double_speed ? 1 : 0), file);
706 fputc((level.gravity ? 1 : 0), file);
708 fputc((encoding_16bit ? 1 : 0), file);
710 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
713 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
715 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
716 fputc(level.author[i], file);
718 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
719 BYTE_ORDER_BIG_ENDIAN);
721 fputc(EL_MAMPFER, file);
722 fputc(level.num_yam_contents, file);
726 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
730 putFile16BitInteger(file, level.yam_content[i][x][y],
731 BYTE_ORDER_BIG_ENDIAN);
733 fputc(level.yam_content[i][x][y], file);
735 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
737 for(y=0; y<lev_fieldy; y++)
738 for(x=0; x<lev_fieldx; x++)
740 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
742 fputc(Ur[x][y], file);
746 chmod(filename, LEVEL_PERMS);
749 void LoadTape(int level_nr)
752 char *filename = getTapeFilename(level_nr);
753 char cookie[MAX_LINE_LEN];
754 char chunk[CHUNK_ID_LEN + 1];
756 int num_participating_players;
757 int file_version = FILE_VERSION_1_2; /* last version of tape files */
760 /* always start with reliable default values (empty tape) */
763 /* default values (also for pre-1.2 tapes) with only the first player */
764 tape.player_participates[0] = TRUE;
765 for(i=1; i<MAX_PLAYERS; i++)
766 tape.player_participates[i] = FALSE;
768 /* at least one (default: the first) player participates in every tape */
769 num_participating_players = 1;
771 if (!(file = fopen(filename, "r")))
774 /* check file identifier */
775 fgets(cookie, MAX_LINE_LEN, file);
776 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
777 cookie[strlen(cookie) - 1] = '\0';
779 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
780 file_version = FILE_VERSION_1_0;
781 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
783 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
788 /* read chunk "HEAD" */
789 if (file_version >= FILE_VERSION_1_2)
791 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
792 if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
794 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
800 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
801 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
802 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
804 /* read header fields that are new since version 1.2 */
805 if (file_version >= FILE_VERSION_1_2)
807 byte store_participating_players = fgetc(file);
809 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
812 /* since version 1.2, tapes store which players participate in the tape */
813 num_participating_players = 0;
814 for(i=0; i<MAX_PLAYERS; i++)
816 tape.player_participates[i] = FALSE;
818 if (store_participating_players & (1 << i))
820 tape.player_participates[i] = TRUE;
821 num_participating_players++;
826 tape.level_nr = level_nr;
828 tape.changed = FALSE;
830 tape.recording = FALSE;
831 tape.playing = FALSE;
832 tape.pausing = FALSE;
834 /* read chunk "BODY" */
835 if (file_version >= FILE_VERSION_1_2)
837 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
838 if (strcmp(chunk, "BODY") ||
839 chunk_length != (num_participating_players + 1) * tape.length)
841 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
847 for(i=0; i<tape.length; i++)
849 if (i >= MAX_TAPELEN)
852 for(j=0; j<MAX_PLAYERS; j++)
854 tape.pos[i].action[j] = MV_NO_MOVING;
856 if (tape.player_participates[j])
857 tape.pos[i].action[j] = fgetc(file);
860 tape.pos[i].delay = fgetc(file);
862 if (file_version == FILE_VERSION_1_0)
864 /* eliminate possible diagonal moves in old tapes */
865 /* this is only for backward compatibility */
867 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
868 byte action = tape.pos[i].action[0];
869 int k, num_moves = 0;
873 if (action & joy_dir[k])
875 tape.pos[i + num_moves].action[0] = joy_dir[k];
877 tape.pos[i + num_moves].delay = 0;
886 tape.length += num_moves;
896 if (i != tape.length)
897 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
899 tape.length_seconds = GetTapeLength();
902 void SaveTape(int level_nr)
905 char *filename = getTapeFilename(level_nr);
907 boolean new_tape = TRUE;
908 byte store_participating_players;
909 int num_participating_players;
911 InitTapeDirectory(leveldir_current->filename);
913 /* if a tape still exists, ask to overwrite it */
914 if (access(filename, F_OK) == 0)
917 if (!Request("Replace old tape ?", REQ_ASK))
921 /* count number of players and set corresponding bits for compact storage */
922 store_participating_players = 0;
923 num_participating_players = 0;
924 for(i=0; i<MAX_PLAYERS; i++)
926 if (tape.player_participates[i])
928 num_participating_players++;
929 store_participating_players |= (1 << i);
933 if (!(file = fopen(filename, "w")))
935 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
939 fputs(TAPE_COOKIE, file); /* file identifier */
942 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
944 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
945 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
946 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
948 fputc(store_participating_players, file);
950 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
953 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
954 BYTE_ORDER_BIG_ENDIAN);
956 for(i=0; i<tape.length; i++)
960 for(j=0; j<MAX_PLAYERS; j++)
961 if (tape.player_participates[j])
962 fputc(tape.pos[i].action[j], file);
964 fputc(tape.pos[i].delay, file);
969 chmod(filename, TAPE_PERMS);
971 tape.changed = FALSE;
974 Request("tape saved !", REQ_CONFIRM);
977 void LoadScore(int level_nr)
980 char *filename = getScoreFilename(level_nr);
981 char cookie[MAX_LINE_LEN];
982 char line[MAX_LINE_LEN];
986 /* always start with reliable default values */
987 for(i=0; i<MAX_SCORE_ENTRIES; i++)
989 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
990 highscore[i].Score = 0;
993 if (!(file = fopen(filename, "r")))
996 /* check file identifier */
997 fgets(cookie, MAX_LINE_LEN, file);
998 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
999 cookie[strlen(cookie) - 1] = '\0';
1001 if (strcmp(cookie, SCORE_COOKIE) != 0)
1003 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1008 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1010 fscanf(file, "%d", &highscore[i].Score);
1011 fgets(line, MAX_LINE_LEN, file);
1013 if (line[strlen(line) - 1] == '\n')
1014 line[strlen(line) - 1] = '\0';
1016 for (line_ptr = line; *line_ptr; line_ptr++)
1018 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1020 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1021 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1030 void SaveScore(int level_nr)
1033 char *filename = getScoreFilename(level_nr);
1036 InitScoreDirectory(leveldir_current->filename);
1038 if (!(file = fopen(filename, "w")))
1040 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1044 fprintf(file, "%s\n\n", SCORE_COOKIE);
1046 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1047 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1051 chmod(filename, SCORE_PERMS);
1054 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1055 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1056 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1057 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1058 #define TOKEN_STR_PLAYER_PREFIX "player_"
1060 #define TOKEN_VALUE_POSITION 30
1063 #define SETUP_TOKEN_PLAYER_NAME 0
1064 #define SETUP_TOKEN_SOUND 1
1065 #define SETUP_TOKEN_SOUND_LOOPS 2
1066 #define SETUP_TOKEN_SOUND_MUSIC 3
1067 #define SETUP_TOKEN_SOUND_SIMPLE 4
1070 #define SETUP_TOKEN_TOONS 5
1071 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1074 #define SETUP_TOKEN_SCROLL_DELAY 5
1075 #define SETUP_TOKEN_SOFT_SCROLLING 6
1076 #define SETUP_TOKEN_FADING 7
1077 #define SETUP_TOKEN_AUTORECORD 8
1078 #define SETUP_TOKEN_QUICK_DOORS 9
1079 #define SETUP_TOKEN_TEAM_MODE 10
1080 #define SETUP_TOKEN_HANDICAP 11
1081 #define SETUP_TOKEN_TIME_LIMIT 12
1082 #define SETUP_TOKEN_FULLSCREEN 13
1085 #define SETUP_TOKEN_USE_JOYSTICK 14
1086 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1087 #define SETUP_TOKEN_JOY_XLEFT 16
1088 #define SETUP_TOKEN_JOY_XMIDDLE 17
1089 #define SETUP_TOKEN_JOY_XRIGHT 18
1090 #define SETUP_TOKEN_JOY_YUPPER 19
1091 #define SETUP_TOKEN_JOY_YMIDDLE 20
1092 #define SETUP_TOKEN_JOY_YLOWER 21
1093 #define SETUP_TOKEN_JOY_SNAP 22
1094 #define SETUP_TOKEN_JOY_BOMB 23
1095 #define SETUP_TOKEN_KEY_LEFT 24
1096 #define SETUP_TOKEN_KEY_RIGHT 25
1097 #define SETUP_TOKEN_KEY_UP 26
1098 #define SETUP_TOKEN_KEY_DOWN 27
1099 #define SETUP_TOKEN_KEY_SNAP 28
1100 #define SETUP_TOKEN_KEY_BOMB 29
1102 /* level directory info */
1103 #define LEVELINFO_TOKEN_NAME 30
1104 #define LEVELINFO_TOKEN_NAME_SHORT 31
1105 #define LEVELINFO_TOKEN_NAME_SORTING 32
1106 #define LEVELINFO_TOKEN_AUTHOR 33
1107 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1108 #define LEVELINFO_TOKEN_LEVELS 35
1109 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1110 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1111 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1112 #define LEVELINFO_TOKEN_READONLY 39
1114 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1115 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1117 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1118 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1120 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1121 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1123 #define TYPE_BOOLEAN 1
1124 #define TYPE_SWITCH 2
1126 #define TYPE_INTEGER 4
1127 #define TYPE_STRING 5
1129 static struct SetupInfo si;
1130 static struct SetupInputInfo sii;
1131 static struct LevelDirInfo ldi;
1140 { TYPE_STRING, &si.player_name, "player_name" },
1141 { TYPE_SWITCH, &si.sound, "sound" },
1142 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1143 { TYPE_SWITCH, &si.sound_music, "background_music" },
1144 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1147 { TYPE_SWITCH, &si.toons, "toons" },
1148 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1151 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1152 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1153 { TYPE_SWITCH, &si.fading, "screen_fading" },
1154 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1155 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1156 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1157 { TYPE_SWITCH, &si.handicap, "handicap" },
1158 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1159 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1162 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1163 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1164 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1165 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1166 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1167 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1168 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1169 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1170 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1171 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1172 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1173 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1174 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1175 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1176 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1177 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1179 /* level directory info */
1180 { TYPE_STRING, &ldi.name, "name" },
1181 { TYPE_STRING, &ldi.name_short, "name_short" },
1182 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1183 { TYPE_STRING, &ldi.author, "author" },
1184 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1185 { TYPE_INTEGER, &ldi.levels, "levels" },
1186 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1187 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1188 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1189 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1192 static char *string_tolower(char *s)
1194 static char s_lower[100];
1197 if (strlen(s) >= 100)
1202 for (i=0; i<strlen(s_lower); i++)
1203 s_lower[i] = tolower(s_lower[i]);
1208 static int get_string_integer_value(char *s)
1210 static char *number_text[][3] =
1212 { "0", "zero", "null", },
1213 { "1", "one", "first" },
1214 { "2", "two", "second" },
1215 { "3", "three", "third" },
1216 { "4", "four", "fourth" },
1217 { "5", "five", "fifth" },
1218 { "6", "six", "sixth" },
1219 { "7", "seven", "seventh" },
1220 { "8", "eight", "eighth" },
1221 { "9", "nine", "ninth" },
1222 { "10", "ten", "tenth" },
1223 { "11", "eleven", "eleventh" },
1224 { "12", "twelve", "twelfth" },
1229 for (i=0; i<13; i++)
1231 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1237 static boolean get_string_boolean_value(char *s)
1239 if (strcmp(string_tolower(s), "true") == 0 ||
1240 strcmp(string_tolower(s), "yes") == 0 ||
1241 strcmp(string_tolower(s), "on") == 0 ||
1242 get_string_integer_value(s) == 1)
1248 static char *getFormattedSetupEntry(char *token, char *value)
1251 static char entry[MAX_LINE_LEN];
1253 sprintf(entry, "%s:", token);
1254 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1258 strcat(entry, value);
1263 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1265 if (!setup_file_list)
1268 if (setup_file_list->token)
1269 free(setup_file_list->token);
1270 if (setup_file_list->value)
1271 free(setup_file_list->value);
1272 if (setup_file_list->next)
1273 freeSetupFileList(setup_file_list->next);
1274 free(setup_file_list);
1277 static struct SetupFileList *newSetupFileList(char *token, char *value)
1279 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1281 new->token = checked_malloc(strlen(token) + 1);
1282 strcpy(new->token, token);
1284 new->value = checked_malloc(strlen(value) + 1);
1285 strcpy(new->value, value);
1292 static char *getTokenValue(struct SetupFileList *setup_file_list,
1295 if (!setup_file_list)
1298 if (strcmp(setup_file_list->token, token) == 0)
1299 return setup_file_list->value;
1301 return getTokenValue(setup_file_list->next, token);
1304 static void setTokenValue(struct SetupFileList *setup_file_list,
1305 char *token, char *value)
1307 if (!setup_file_list)
1310 if (strcmp(setup_file_list->token, token) == 0)
1312 free(setup_file_list->value);
1313 setup_file_list->value = checked_malloc(strlen(value) + 1);
1314 strcpy(setup_file_list->value, value);
1316 else if (setup_file_list->next == NULL)
1317 setup_file_list->next = newSetupFileList(token, value);
1319 setTokenValue(setup_file_list->next, token, value);
1323 static void printSetupFileList(struct SetupFileList *setup_file_list)
1325 if (!setup_file_list)
1328 printf("token: '%s'\n", setup_file_list->token);
1329 printf("value: '%s'\n", setup_file_list->value);
1331 printSetupFileList(setup_file_list->next);
1335 static struct SetupFileList *loadSetupFileList(char *filename)
1338 char line[MAX_LINE_LEN];
1339 char *token, *value, *line_ptr;
1340 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1341 struct SetupFileList *first_valid_list_entry;
1345 if (!(file = fopen(filename, "r")))
1347 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1353 /* read next line of input file */
1354 if (!fgets(line, MAX_LINE_LEN, file))
1357 /* cut trailing comment or whitespace from input line */
1358 for (line_ptr = line; *line_ptr; line_ptr++)
1360 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1367 /* cut trailing whitespaces from input line */
1368 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1369 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1372 /* ignore empty lines */
1376 line_len = strlen(line);
1378 /* cut leading whitespaces from token */
1379 for (token = line; *token; token++)
1380 if (*token != ' ' && *token != '\t')
1383 /* find end of token */
1384 for (line_ptr = token; *line_ptr; line_ptr++)
1386 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1393 if (line_ptr < line + line_len)
1394 value = line_ptr + 1;
1398 /* cut leading whitespaces from value */
1399 for (; *value; value++)
1400 if (*value != ' ' && *value != '\t')
1403 if (*token && *value)
1404 setTokenValue(setup_file_list, token, value);
1409 first_valid_list_entry = setup_file_list->next;
1411 /* free empty list header */
1412 setup_file_list->next = NULL;
1413 freeSetupFileList(setup_file_list);
1415 if (first_valid_list_entry == NULL)
1416 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1418 return first_valid_list_entry;
1421 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1424 if (!setup_file_list)
1427 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1429 if (strcmp(setup_file_list->value, identifier) != 0)
1431 Error(ERR_WARN, "configuration file has wrong version");
1438 if (setup_file_list->next)
1439 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1442 Error(ERR_WARN, "configuration file has no version information");
1447 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1449 ldi->filename = NULL;
1450 ldi->fullpath = NULL;
1451 ldi->basepath = NULL;
1452 ldi->name = getStringCopy(ANONYMOUS_NAME);
1453 ldi->name_short = NULL;
1454 ldi->name_sorting = NULL;
1455 ldi->author = getStringCopy(ANONYMOUS_NAME);
1456 ldi->imported_from = NULL;
1458 ldi->first_level = 0;
1459 ldi->last_level = 0;
1460 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1461 ldi->level_group = FALSE;
1462 ldi->parent_link = FALSE;
1463 ldi->user_defined = FALSE;
1464 ldi->readonly = TRUE;
1466 ldi->class_desc = NULL;
1467 ldi->handicap_level = 0;
1469 ldi->cl_cursor = -1;
1471 ldi->node_parent = NULL;
1472 ldi->node_group = NULL;
1476 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1477 struct LevelDirInfo *parent)
1481 setLevelDirInfoToDefaults(ldi);
1485 /* first copy all values from the parent structure ... */
1488 /* ... then set all fields to default that cannot be inherited from parent.
1489 This is especially important for all those fields that can be set from
1490 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1491 calls 'free()' for all already set token values which requires that no
1492 other structure's pointer may point to them!
1495 ldi->filename = NULL;
1496 ldi->fullpath = NULL;
1497 ldi->basepath = NULL;
1498 ldi->name = getStringCopy(ANONYMOUS_NAME);
1499 ldi->name_short = NULL;
1500 ldi->name_sorting = NULL;
1501 ldi->author = getStringCopy(parent->author);
1502 ldi->imported_from = getStringCopy(parent->imported_from);
1504 ldi->level_group = FALSE;
1505 ldi->parent_link = FALSE;
1507 ldi->node_parent = parent;
1508 ldi->node_group = NULL;
1512 static void setSetupInfoToDefaults(struct SetupInfo *si)
1516 si->player_name = getStringCopy(getLoginName());
1519 si->sound_loops = TRUE;
1520 si->sound_music = TRUE;
1521 si->sound_simple = TRUE;
1523 si->double_buffering = TRUE;
1524 si->direct_draw = !si->double_buffering;
1525 si->scroll_delay = TRUE;
1526 si->soft_scrolling = TRUE;
1528 si->autorecord = TRUE;
1529 si->quick_doors = FALSE;
1530 si->team_mode = FALSE;
1531 si->handicap = TRUE;
1532 si->time_limit = TRUE;
1533 si->fullscreen = FALSE;
1535 for (i=0; i<MAX_PLAYERS; i++)
1537 si->input[i].use_joystick = FALSE;
1538 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1539 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1540 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1541 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1542 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1543 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1544 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1545 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1546 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1547 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1548 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1549 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1550 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1551 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1552 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1556 static void setSetupInfo(int token_nr, char *token_value)
1558 int token_type = token_info[token_nr].type;
1559 void *setup_value = token_info[token_nr].value;
1561 if (token_value == NULL)
1564 /* set setup field to corresponding token value */
1569 *(boolean *)setup_value = get_string_boolean_value(token_value);
1573 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1577 *(int *)setup_value = get_string_integer_value(token_value);
1581 if (*(char **)setup_value != NULL)
1582 free(*(char **)setup_value);
1583 *(char **)setup_value = getStringCopy(token_value);
1591 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1595 if (!setup_file_list)
1598 /* handle global setup values */
1600 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1601 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1604 /* handle player specific setup values */
1605 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1609 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1611 sii = setup.input[pnr];
1612 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1614 char full_token[100];
1616 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1617 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1619 setup.input[pnr] = sii;
1623 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1625 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1626 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1629 if (entry1->parent_link || entry2->parent_link)
1630 compare_result = (entry1->parent_link ? -1 : +1);
1631 else if (entry1->sort_priority == entry2->sort_priority)
1633 char *name1 = getStringToLower(entry1->name_sorting);
1634 char *name2 = getStringToLower(entry2->name_sorting);
1636 compare_result = strcmp(name1, name2);
1641 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1642 compare_result = entry1->sort_priority - entry2->sort_priority;
1644 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1646 return compare_result;
1649 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1651 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1653 setLevelDirInfoToDefaults(leveldir_new);
1655 leveldir_new->node_parent = node_parent;
1656 leveldir_new->parent_link = TRUE;
1658 leveldir_new->name = ".. (parent directory)";
1659 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1660 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1662 leveldir_new->filename = "..";
1663 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1665 leveldir_new->sort_priority = node_parent->sort_priority;
1666 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1668 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1671 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1672 struct LevelDirInfo *node_parent,
1673 char *level_directory)
1676 struct dirent *dir_entry;
1677 boolean valid_entry_found = FALSE;
1679 if ((dir = opendir(level_directory)) == NULL)
1681 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1685 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1687 struct SetupFileList *setup_file_list = NULL;
1688 struct stat file_status;
1689 char *directory_name = dir_entry->d_name;
1690 char *directory_path = getPath2(level_directory, directory_name);
1691 char *filename = NULL;
1693 /* skip entries for current and parent directory */
1694 if (strcmp(directory_name, ".") == 0 ||
1695 strcmp(directory_name, "..") == 0)
1697 free(directory_path);
1701 /* find out if directory entry is itself a directory */
1702 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1703 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1705 free(directory_path);
1709 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1710 setup_file_list = loadSetupFileList(filename);
1712 if (setup_file_list)
1714 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1717 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1718 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1720 /* set all structure fields according to the token/value pairs */
1721 ldi = *leveldir_new;
1722 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1723 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1724 *leveldir_new = ldi;
1726 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1728 if (leveldir_new->name_short == NULL)
1729 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1731 if (leveldir_new->name_sorting == NULL)
1732 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1734 leveldir_new->filename = getStringCopy(directory_name);
1736 if (node_parent == NULL) /* top level group */
1738 leveldir_new->basepath = level_directory;
1739 leveldir_new->fullpath = leveldir_new->filename;
1741 else /* sub level group */
1743 leveldir_new->basepath = node_parent->basepath;
1744 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1748 if (leveldir_new->levels < 1)
1749 leveldir_new->levels = 1;
1751 leveldir_new->last_level =
1752 leveldir_new->first_level + leveldir_new->levels - 1;
1754 leveldir_new->user_defined =
1755 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1757 leveldir_new->color = LEVELCOLOR(leveldir_new);
1758 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1760 leveldir_new->handicap_level = /* set handicap to default value */
1761 (leveldir_new->user_defined ?
1762 leveldir_new->last_level :
1763 leveldir_new->first_level);
1765 pushLevelDirInfo(node_first, leveldir_new);
1767 freeSetupFileList(setup_file_list);
1768 valid_entry_found = TRUE;
1770 if (leveldir_new->level_group)
1772 /* create node to link back to current level directory */
1773 createParentLevelDirNode(leveldir_new);
1775 /* step into sub-directory and look for more level series */
1776 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1777 leveldir_new, directory_path);
1781 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1783 free(directory_path);
1789 if (!valid_entry_found)
1790 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1794 void LoadLevelInfo()
1796 InitUserLevelDirectory(getLoginName());
1798 DrawInitText("Loading level series:", 120, FC_GREEN);
1800 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1801 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1803 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1805 if (leveldir_first == NULL)
1806 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1808 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1811 dumpLevelDirInfo(leveldir_first, 0);
1815 static void SaveUserLevelInfo()
1821 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1823 if (!(file = fopen(filename, "w")))
1825 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1830 /* always start with reliable default values */
1831 setLevelDirInfoToDefaults(&ldi);
1833 ldi.name = getLoginName();
1834 ldi.author = getRealName();
1836 ldi.first_level = 1;
1837 ldi.sort_priority = LEVELCLASS_USER_START;
1838 ldi.readonly = FALSE;
1840 fprintf(file, "%s\n\n",
1841 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1843 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1844 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1845 i != LEVELINFO_TOKEN_NAME_SORTING &&
1846 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1847 fprintf(file, "%s\n", getSetupLine("", i));
1852 chmod(filename, SETUP_PERMS);
1858 struct SetupFileList *setup_file_list = NULL;
1860 /* always start with reliable default values */
1861 setSetupInfoToDefaults(&setup);
1863 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1865 setup_file_list = loadSetupFileList(filename);
1867 if (setup_file_list)
1869 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1870 decodeSetupFileList(setup_file_list);
1872 setup.direct_draw = !setup.double_buffering;
1874 freeSetupFileList(setup_file_list);
1876 /* needed to work around problems with fixed length strings */
1877 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1878 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1879 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1881 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1883 strcpy(new_name, setup.player_name);
1884 free(setup.player_name);
1885 setup.player_name = new_name;
1889 Error(ERR_WARN, "using default setup values");
1894 static char *getSetupLine(char *prefix, int token_nr)
1897 static char entry[MAX_LINE_LEN];
1898 int token_type = token_info[token_nr].type;
1899 void *setup_value = token_info[token_nr].value;
1900 char *token_text = token_info[token_nr].text;
1902 /* start with the prefix, token and some spaces to format output line */
1903 sprintf(entry, "%s%s:", prefix, token_text);
1904 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1907 /* continue with the token's value (which can have different types) */
1911 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1915 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1920 Key key = *(Key *)setup_value;
1921 char *keyname = getKeyNameFromKey(key);
1923 strcat(entry, getX11KeyNameFromKey(key));
1924 for (i=strlen(entry); i<50; i++)
1927 /* add comment, if useful */
1928 if (strcmp(keyname, "(undefined)") != 0 &&
1929 strcmp(keyname, "(unknown)") != 0)
1931 strcat(entry, "# ");
1932 strcat(entry, keyname);
1939 char buffer[MAX_LINE_LEN];
1941 sprintf(buffer, "%d", *(int *)setup_value);
1942 strcat(entry, buffer);
1947 strcat(entry, *(char **)setup_value);
1963 InitUserDataDirectory();
1965 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1967 if (!(file = fopen(filename, "w")))
1969 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1974 fprintf(file, "%s\n",
1975 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1976 fprintf(file, "\n");
1978 /* handle global setup values */
1980 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1982 fprintf(file, "%s\n", getSetupLine("", i));
1984 /* just to make things nicer :) */
1985 if (i == SETUP_TOKEN_PLAYER_NAME)
1986 fprintf(file, "\n");
1989 /* handle player specific setup values */
1990 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1994 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1995 fprintf(file, "\n");
1997 sii = setup.input[pnr];
1998 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1999 fprintf(file, "%s\n", getSetupLine(prefix, i));
2005 chmod(filename, SETUP_PERMS);
2008 void LoadLevelSetup_LastSeries()
2011 struct SetupFileList *level_setup_list = NULL;
2013 /* always start with reliable default values */
2014 leveldir_current = leveldir_first;
2016 /* ----------------------------------------------------------------------- */
2017 /* ~/.rocksndiamonds/levelsetup.conf */
2018 /* ----------------------------------------------------------------------- */
2020 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2022 if ((level_setup_list = loadSetupFileList(filename)))
2024 char *last_level_series =
2025 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2027 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2028 if (leveldir_current == NULL)
2029 leveldir_current = leveldir_first;
2031 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2033 freeSetupFileList(level_setup_list);
2036 Error(ERR_WARN, "using default setup values");
2041 void SaveLevelSetup_LastSeries()
2044 char *level_subdir = leveldir_current->filename;
2047 /* ----------------------------------------------------------------------- */
2048 /* ~/.rocksndiamonds/levelsetup.conf */
2049 /* ----------------------------------------------------------------------- */
2051 InitUserDataDirectory();
2053 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2055 if (!(file = fopen(filename, "w")))
2057 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2062 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2063 LEVELSETUP_COOKIE));
2064 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2070 chmod(filename, SETUP_PERMS);
2073 static void checkSeriesInfo()
2075 static char *level_directory = NULL;
2077 struct dirent *dir_entry;
2079 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2081 level_directory = getPath2((leveldir_current->user_defined ?
2082 getUserLevelDir("") :
2083 options.level_directory),
2084 leveldir_current->fullpath);
2086 if ((dir = opendir(level_directory)) == NULL)
2088 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2092 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2094 if (strlen(dir_entry->d_name) > 4 &&
2095 dir_entry->d_name[3] == '.' &&
2096 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2098 char levelnum_str[4];
2101 strncpy(levelnum_str, dir_entry->d_name, 3);
2102 levelnum_str[3] = '\0';
2104 levelnum_value = atoi(levelnum_str);
2106 if (levelnum_value < leveldir_current->first_level)
2108 Error(ERR_WARN, "additional level %d found", levelnum_value);
2109 leveldir_current->first_level = levelnum_value;
2111 else if (levelnum_value > leveldir_current->last_level)
2113 Error(ERR_WARN, "additional level %d found", levelnum_value);
2114 leveldir_current->last_level = levelnum_value;
2122 void LoadLevelSetup_SeriesInfo()
2125 struct SetupFileList *level_setup_list = NULL;
2126 char *level_subdir = leveldir_current->filename;
2128 /* always start with reliable default values */
2129 level_nr = leveldir_current->first_level;
2131 checkSeriesInfo(leveldir_current);
2133 /* ----------------------------------------------------------------------- */
2134 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2135 /* ----------------------------------------------------------------------- */
2137 level_subdir = leveldir_current->filename;
2139 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2141 if ((level_setup_list = loadSetupFileList(filename)))
2145 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2149 level_nr = atoi(token_value);
2151 if (level_nr < leveldir_current->first_level)
2152 level_nr = leveldir_current->first_level;
2153 if (level_nr > leveldir_current->last_level)
2154 level_nr = leveldir_current->last_level;
2157 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2161 int level_nr = atoi(token_value);
2163 if (level_nr < leveldir_current->first_level)
2164 level_nr = leveldir_current->first_level;
2165 if (level_nr > leveldir_current->last_level + 1)
2166 level_nr = leveldir_current->last_level;
2168 if (leveldir_current->user_defined)
2169 level_nr = leveldir_current->last_level;
2171 leveldir_current->handicap_level = level_nr;
2174 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2176 freeSetupFileList(level_setup_list);
2179 Error(ERR_WARN, "using default setup values");
2184 void SaveLevelSetup_SeriesInfo()
2187 char *level_subdir = leveldir_current->filename;
2188 char *level_nr_str = int2str(level_nr, 0);
2189 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2192 /* ----------------------------------------------------------------------- */
2193 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2194 /* ----------------------------------------------------------------------- */
2196 InitLevelSetupDirectory(level_subdir);
2198 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2200 if (!(file = fopen(filename, "w")))
2202 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2207 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2208 LEVELSETUP_COOKIE));
2209 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2211 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2212 handicap_level_str));
2217 chmod(filename, SETUP_PERMS);
2220 #if !defined(PLATFORM_UNIX)
2221 void initErrorFile()
2225 InitUserDataDirectory();
2227 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2232 FILE *openErrorFile()
2237 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2238 error_file = fopen(filename, "a");
2244 void dumpErrorFile()
2249 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2250 error_file = fopen(filename, "r");
2253 if (error_file != NULL)
2255 while (!feof(error_file))
2256 fputc(fgetc(error_file), stderr);